定义
状态模式(State),当一个对象的内部状态改变时允许改变其行为,这个对象看起来像是改变了其类。
类图
- Context:环境,上下文,其实就是状态管理者,隐藏了状态之间转换的细节,是客户端与各个状态的中间人。request()方法是客户端调用的,
- State:抽象状态,定义了状态的处理抽象方法handle(),各个具体状态需要实现它。
- ConcreteState:具体状态,实现了状态的公共方法,且包含了状态转换的判断。
经典源码
状态管理者
public class Context {
//持有一个State类型的对象实例
private State state;
public void setState(State state) {
this.state = state;
}
/**
* 客户端调用
*/
public void request(String sampleParameter) {
//转调state来处理
state.handle(sampleParameter);
}
}
抽象状态
public interface State {
/**
* 状态对应的处理
*/
public void handle(String sampleParameter);
}
具体状态A
public class ConcreteStateA implements State {
@Override
public void handle(String sampleParameter) {
System.out.println("ConcreteStateA handle :" + sampleParameter);
}
}
具体状态B
public class ConcreteStateB implements State {
@Override
public void handle(String sampleParameter) {
System.out.println("ConcreteStateB handle :" + sampleParameter);
}
}
客户端调用方
public class Client {
public static void main(String[] args){
//创建状态
State state = new ConcreteStateB();
//创建环境
Context context = new Context();
//将状态设置到环境中
context.setState(state);
//请求
context.request("test");
}
}
实际案例
背景
小A正在召唤师峡谷厮杀,他玩的是卡特,买了一本杀人书。随着游戏时间的进行,小A杀的人越来越多,且自己都没死过,游戏中不断传来“XXX暴走了”、“XXX主宰了比赛”、“XXX接近神了”。最终小A以12杀0死的完美战绩结束了比赛,脑海中刚才的捷报声还在不停环绕在他的耳边,他就想这不就是状态模式,随后打开IDE开始复现刚才的过程。
结构
环境类
package com.jo.state;
/**
* @author Jo
* @date 2018/1/17
*/
public class LolContext {
/**
* 当前杀人状态
*/
private KillState killState;
/**
* 当前杀人数
*/
private Integer killNum = 0;
public LolContext() {
killState = new Normal();
}
public Integer getKillNum() {
return killNum;
}
public LolContext setKillState(KillState killState) {
this.killState = killState;
return this;
}
/**
* 杀人方法,
*/
public void kill(){
killNum += 1;
killState.kill(this);
System.out.println("当前击杀数" + killNum);
System.out.println();
}
}
抽象杀人状态
public interface KillState {
/**
* 抽象杀人方法
* @param lolContext
*/
void kill(LolContext lolContext);
}
普通击杀
public class Normal implements KillState {
@Override
public void kill(LolContext lolContext) {
System.out.println("你杀了一个人");
//杀了2个人的时候转换状态
if (lolContext.getKillNum() > 1){
lolContext.setKillState(new KillingSpring());
}
}
}
大杀特杀
public class KillingSpring implements KillState {
@Override
public void kill(LolContext lolContext) {
System.out.println("你正在大杀特杀");
lolContext.setKillState(new Rampage());
}
}
接近暴走
public class Rampage implements KillState {
@Override
public void kill(LolContext lolContext) {
System.out.println("你已经接近暴走了");
lolContext.setKillState(new Unstoppable());
}
}
无人可挡
public class Unstoppable implements KillState {
@Override
public void kill(LolContext lolContext) {
System.out.println("你已经无人可挡了");
lolContext.setKillState(new Dominating());
}
}
、
、
、
、
超神
public class Legendary implements KillState {
@Override
public void kill(LolContext lolContext) {
System.out.println("你已经超神了");
}
}
客户端调用
public class Client {
public static void main(String[] args) {
LolContext lolContext = new LolContext();
for (int i = 0; i < 13; i++) {
lolContext.kill();
}
}
}
运行结果
客户端只负责调用LolContext的kill方法,其余一概不知,内部便会随着杀人数的增长打印不同的通知。而LolContext中也没有复杂,一大串的if else switch case,状态的转移在各自的具体状态中,如果需要修改部分逻辑只需要改对应的状态,而不需要改原本在一起的if else,大大减少了隐患的发生率,从而不会发生牵一发而动全身的结果。
适用场景
状态模式的优点是解除了程序的耦合度,采用子类的方式去除了烦琐容易出错的if else,但反而带来的是类的数据增多。如果你要实现的功能状态不多,且功能简单,那不推荐使用状态模式,不然会徒增程序的复杂性。且要执行的动作有一定的复杂度,此例的kill方法是最简单的实现,实际应用中复杂度是远远大于它的。你可以想象在状态多,且复杂的动作中不使用状态模式会事怎样,if else多的眼花缭乱,上一个if和下一个else if相差几百行代码,想必这样的代码谁都不愿意碰,万一改坏了就要背锅了。
总结
状态模式和和策略模式有点相像,状态模式的状态转移是内部控制的,而策略模式是由客户端控制采用不同的策略。因此在目的和实现还是有很大的差别的。有些场景“状态”不是那么明显,需要转换成状态模式就考察使用者的功底和对业务的理解程度了,望大家都能get更多知识点,能力越来越强,个个都是架构师。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。