设计模式(17)——状态模式

本文介绍状态模式的概念和应用。

基本思想和原则

当一个对象内在状态改变时允许改变其行为,这个对象看起来像改变了其类。

动机

当某个对象的状态改变时,其行为也会发生改变,就可以考虑使用状态模式来处理。

实现

我们用一个水三态转换的例子来说明状态模式,正常情况水有三种形态:液态、固态和气态,分别对应水、冰和水蒸气。水的三态转换过程如下图:

水的三态转换

三种状态之间可以任意转换,我们下面就模拟这个过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
public abstract class WaterState {
protected Context context;

public void setContext(Context context) {
this.context = context;
}

// 液化
public abstract void liquefy();

// 汽化
public abstract void evaporate();

// 凝固
public abstract void freeze();

// 熔化
public abstract void melt();

// 凝华
public abstract void desublimate();

// 升华
public abstract void sublimate();
}

public class LiquidState extends WaterState {
@Override
public void liquefy() {
System.out.println("水蒸气->水:液化过程");
}

@Override
public void evaporate() {
this.context.setCurrentState(Context.gasState);
this.context.getCurrentState().evaporate();
}

@Override
public void freeze() {
this.context.setCurrentState(Context.solidState);
this.context.getCurrentState().freeze();
}

@Override
public void melt() {
System.out.println("冰->水:熔化过程");
}

@Override
public void desublimate() {
// do nothing
}

@Override
public void sublimate() {
// do nothing
}
}

public class SolidState extends WaterState {
@Override
public void liquefy() {
// do nothing
}

@Override
public void evaporate() {
// do nothing
}

@Override
public void freeze() {
System.out.println("水->冰:凝固过程");
}

@Override
public void melt() {
this.context.setCurrentState(Context.liquidState);
this.context.getCurrentState().melt();
}

@Override
public void desublimate() {
System.out.println("水蒸气->冰:凝华过程");
}

@Override
public void sublimate() {
this.context.setCurrentState(Context.gasState);
this.context.getCurrentState().sublimate();
}
}

public class GasState extends WaterState {
@Override
public void liquefy() {
this.context.setCurrentState(Context.liquidState);
this.context.getCurrentState().liquefy();
}

@Override
public void evaporate() {
System.out.println("水->水蒸气:气化过程");
}

@Override
public void freeze() {
// do nothing
}

@Override
public void melt() {
// do nothind
}

@Override
public void desublimate() {
this.context.setCurrentState(Context.solidState);
this.context.getCurrentState().desublimate();
}

@Override
public void sublimate() {
System.out.println("冰->水蒸气:升华过程");
}
}

public class Context {
private WaterState currentState;
public final static WaterState liquidState = new LiquidState();
public final static WaterState solidState = new SolidState();
public final static WaterState gasState = new GasState();

public void setCurrentState(WaterState currentState) {
this.currentState = currentState;
this.currentState.setContext(this);
}

public WaterState getCurrentState() {
return this.currentState;
}

public void liquefy() {
this.currentState.liquefy();
}

public void evaporate() {
this.currentState.evaporate();
}

public void freeze() {
this.currentState.freeze();
}

public void melt() {
this.currentState.melt();
}

public void desublimate() {
this.currentState.desublimate();
}

public void sublimate() {
this.currentState.sublimate();
}
}

public class Test {
public static void main(String[] args) {
Context context = new Context();
context.setCurrentState(new LiquidState());

context.evaporate(); // 水->水蒸气:汽化过程
context.desublimate(); // 水蒸气->冰:凝华过程
context.melt(); // 冰->水:熔化过程
context.freeze(); // 水->冰:凝固过程
context.sublimate(); // 冰->水蒸气:升华过程
context.liquefy(); // 水蒸气->水:液化过程
}
}

输出如下:

1
2
3
4
5
6
水->水蒸气:气化过程
水蒸气->冰:凝华过程
冰->水:熔化过程
水->冰:凝固过程
冰->水蒸气:升华过程
水蒸气->水:液化过程

简单分析一下这个例子,我们有一个State的继承体系,WaterState是一个抽象类,定义了所有状态间转换的方法,并且在内部持有一个Context类实例,这个实例在状态转换过程中会使用到。LiquidStateSolidStateGasState是具体的状态类,需要实现WaterState定义的所有抽象方法。Context类持有一个状态实例currentState,表示水当前的状态,很重要的一点是Context类中也定义了所有状态转换的方法,调用时会委托给currentState来处理。

优点

状态模式避免了过多的switch-case和if-else语句,代码清晰。符合开闭原则、单一职责原则,当需要增加状态时,只需要增加子类,不需要修改原有的代码。封装性很好,外部不需要了解状态之间具体是如何转换的,所有的状态间过渡方式都由各个状态子类负责。

缺点

状态模式的缺点是有可能会导致过多的状态子类。