状态模式主要应用在游戏、工作流引擎中,其实就是有限状态机的实现,目前开发中还没有遇到过,此处留坑。

但状态模式也比较有意思,它可以将过多的 if...else... 或者 switch...case... 抽离出来,使得代码的扩展性更好一些。

举个例子,详细解释可以查看极客时间的 设计模式之美 讲的。

一个超级马里奥的例子,吃了蘑菇、吃了花都会有不同的状态进行转移。

img

1public enum State {
2  SMALL(0),
3  SUPER(1),
4  FIRE(2),
5  CAPE(3);
6
7  private int value;
8
9  private State(int value) {
10    this.value = value;
11  }
12
13  public int getValue() {
14    return this.value;
15  }
16}
17
18public class MarioStateMachine {
19  private int score;
20  private State currentState;
21
22  public MarioStateMachine() {
23    this.score = 0;
24    this.currentState = State.SMALL;
25  }
26
27  public void obtainMushRoom() {
28    if (currentState.equals(State.SMALL)) {
29      this.currentState = State.SUPER;
30      this.score += 100;
31    }
32  }
33
34  public void obtainCape() {
35    if (currentState.equals(State.SMALL) || currentState.equals(State.SUPER) ) {
36      this.currentState = State.CAPE;
37      this.score += 200;
38    }
39  }
40
41  public void obtainFireFlower() {
42    if (currentState.equals(State.SMALL) || currentState.equals(State.SUPER) ) {
43      this.currentState = State.FIRE;
44      this.score += 300;
45    }
46  }
47
48  public void meetMonster() {
49    if (currentState.equals(State.SUPER)) {
50      this.currentState = State.SMALL;
51      this.score -= 100;
52      return;
53    }
54
55    if (currentState.equals(State.CAPE)) {
56      this.currentState = State.SMALL;
57      this.score -= 200;
58      return;
59    }
60
61    if (currentState.equals(State.FIRE)) {
62      this.currentState = State.SMALL;
63      this.score -= 300;
64      return;
65    }
66  }
67
68  public int getScore() {
69    return this.score;
70  }
71
72  public State getCurrentState() {
73    return this.currentState;
74  }
75}
76
77public class ApplicationDemo {
78  public static void main(String[] args) {
79    MarioStateMachine mario = new MarioStateMachine();
80    mario.obtainMushRoom();
81    int score = mario.getScore();
82    State state = mario.getCurrentState();
83    System.out.println("mario score: " + score + "; state: " + state);
84  }
85}

上边的实现没什么问题,通过 if 来进行状态的转移,并进行相关操作,但可维护性和扩展性都很差。

我们可以通过状态模式进行改写,将每一种状态抽离出来,将状态的变更委托给状态类实现,原来的类不再处理。

1public interface IMario { //所有状态类的接口
2  State getName();
3  //以下是定义的事件
4  void obtainMushRoom();
5  void obtainCape();
6  void obtainFireFlower();
7  void meetMonster();
8}
9
10public class SmallMario implements IMario {
11  private MarioStateMachine stateMachine;
12
13  public SmallMario(MarioStateMachine stateMachine) {
14    // 持有原类的引用,来帮助原类更新状态,相当于将上下文传递过来,可以直接调用上下文的方法
15    this.stateMachine = stateMachine;
16  }
17
18  @Override
19  public State getName() {
20    return State.SMALL;
21  }
22
23  @Override
24  public void obtainMushRoom() {
25    stateMachine.setCurrentState(new SuperMario(stateMachine));
26    stateMachine.setScore(stateMachine.getScore() + 100);
27  }
28
29  @Override
30  public void obtainCape() {
31    // 更新原类中的状态类
32    stateMachine.setCurrentState(new CapeMario(stateMachine));
33    stateMachine.setScore(stateMachine.getScore() + 200);
34  }
35
36  @Override
37  public void obtainFireFlower() {
38    stateMachine.setCurrentState(new FireMario(stateMachine));
39    stateMachine.setScore(stateMachine.getScore() + 300);
40  }
41
42  @Override
43  public void meetMonster() {
44    // do nothing...
45  }
46}
47
48public class SuperMario implements IMario {
49  private MarioStateMachine stateMachine;
50
51  public SuperMario(MarioStateMachine stateMachine) {
52    this.stateMachine = stateMachine;
53  }
54
55  @Override
56  public State getName() {
57    return State.SUPER;
58  }
59
60  @Override
61  public void obtainMushRoom() {
62    // do nothing...
63  }
64
65  @Override
66  public void obtainCape() {
67    stateMachine.setCurrentState(new CapeMario(stateMachine));
68    stateMachine.setScore(stateMachine.getScore() + 200);
69  }
70
71  @Override
72  public void obtainFireFlower() {
73    stateMachine.setCurrentState(new FireMario(stateMachine));
74    stateMachine.setScore(stateMachine.getScore() + 300);
75  }
76
77  @Override
78  public void meetMonster() {
79    stateMachine.setCurrentState(new SmallMario(stateMachine));
80    stateMachine.setScore(stateMachine.getScore() - 100);
81  }
82}
83
84// 省略CapeMario、FireMario类...
85
86public class MarioStateMachine {
87  private int score;
88  // 包含一个状态类的引用
89  private IMario currentState; // 不再使用枚举来表示状态
90
91  public MarioStateMachine() {
92    this.score = 0;
93    this.currentState = new SmallMario(this);
94  }
95
96  public void obtainMushRoom() {
97    // 委托给状态类执行
98    this.currentState.obtainMushRoom();
99  }
100
101  public void obtainCape() {
102    this.currentState.obtainCape();
103  }
104
105  public void obtainFireFlower() {
106    this.currentState.obtainFireFlower();
107  }
108
109  public void meetMonster() {
110    this.currentState.meetMonster();
111  }
112
113  public int getScore() {
114    return this.score;
115  }
116
117  public State getCurrentState() {
118    return this.currentState.getName();
119  }
120
121  public void setScore(int score) {
122    this.score = score;
123  }
124
125  public void setCurrentState(IMario currentState) {
126    this.currentState = currentState;
127  }
128}

状态模式很巧妙,虽然实际开发中还没应用到,但还是很有意思的。