设计模式(16):状态模式STATE

状态模式

状态模式(Strategy Pattern)又名状态对象(Objects for States)

意图

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。


使用场景

当出现下面的两种情况之一时使用State模式

  • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

结构

状态模式结构如下
ShowImage

  • Context

    – 定义Client所关注的接口。

    – 维护一个ConcreteState子类的实例,这个实例定义当前状态。

  • State

    – 定义一个接口以封装与Context的一个特定状态相关的行为。

  • ConcreteState subclasses

    – 每一子类实现一个与Context的一个状态相关的行为。


协作

  • Context将与状态相关的请求委托给当前的ConcreteState对象处理。
  • Context可将自身作为一个参数传递给处理该请求的状态对象。这使得状态对象在必要时可访问Context。
  • Context是客户使用的主要接口。客户可用状态对象来配置一个Context,一旦一个Context配置完毕, 它的Client不再需要直接与状态对象打交道。
  • Context或ConcreteState子类都可决定哪个状态是另外哪一个的后继者,以及是在何种条件下进行状态转换。

效果

State模式有下面一些效果

  • 它将与特定状态相关的行为局部化,并且将不同状态的行为分割开来

    – State模式将所有与一个特定的状态相关的行为都放入一个对象中。因为所有与状态相关的代码都存在于某一个State子类中, 所以通过定义新的子类可以很容易的增加新的状态和转换。

    – State模式避免使用大量的ifelse语句或者switchcase语句来控制状态,但是可能会引入更多的类,增加了子类的数目
  • 它使得状态转换显式化

    – 当一个对象仅以内部数据值来定义当前状态时, 其状态仅表现为对一些变量的赋值,这不够明确。为不同的状态引入独立的对象使得转换变得更加明确。而且, State对象可保证Context不会发生内部状态不一致的情况,因为从Context的角度看,状态转换是原子的–只需重新绑定一个变量(即Context的State对象变量),而无需为多个变量赋值。
  • State对象可被共享

    – 如果State对象没有实例变量–即它们表示的状态完全以它们的类型来编码–那么各Context对象可以共享一个State对象。当状态以这种方式被共享时, 它们必然是没有内部状态, 只有行为的轻量级对象(Flyweight Pattern)。

状态模式实现(Implement)

案例

F1比赛大家应该都看过,作为世界上最烧钱的比赛,一辆车就动辄几百万。F1赛车的变速箱也非常特别,赛车手只需要拨动方向盘上的拨片就可以进行换挡,看上去十分酷炫。现在用代码来实现这个换挡过程。

代码实现

先来看下面一段代码

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
/*F1变速器一共有空挡,1-7前进档和倒挡,升档操作upShift,降档操作downShift*/
public class GearShift {
private static final int NEUTRAL_GEAR = 0;
private static final int FIRST_FORWARD_GEAR = 1;
private static final int SECOND_FORWARD_GEAR = 2;
private static final int THIRD_FORWARD_GEAR = 3;
private static final int FOURTH_FORWARD_GEAR = 4;
private static final int FIFTH_FORWARD_GEAR = 5;
private static final int SIXTH_FORWARD_GEAR = 6;
private static final int SEVENTH_FORWARD_GEAR = 7;
private static final int REVERSE_GEAR = -1;
private int gearState = 0;
public void upShift() {
if (gearState == REVERSE_GEAR) {
gearState = NEUTRAL_GEAR;
System.out.println("从倒车档挂入空挡...");
}
if (gearState == NEUTRAL_GEAR) {
gearState = FIRST_FORWARD_GEAR;
System.out.println("从空档挂入1档...");
}
if (gearState == FIRST_FORWARD_GEAR) {
gearState = SECOND_FORWARD_GEAR;
System.out.println("从1档挂入2档...");
}
if (gearState == SECOND_FORWARD_GEAR) {
gearState = THIRD_FORWARD_GEAR;
System.out.println("从2档挂入3档...");
}
if (gearState == THIRD_FORWARD_GEAR) {
gearState = FOURTH_FORWARD_GEAR;
System.out.println("从3档挂入4档...");
}
if (gearState == FOURTH_FORWARD_GEAR) {
gearState = FIFTH_FORWARD_GEAR;
System.out.println("从4档挂入5档...");
}
if (gearState == FIFTH_FORWARD_GEAR) {
gearState = SIXTH_FORWARD_GEAR;
System.out.println("从5档挂入6档...");
}
if (gearState == SIXTH_FORWARD_GEAR) {
gearState = SEVENTH_FORWARD_GEAR;
System.out.println("从6档挂入7档...");
}
if (gearState == SEVENTH_FORWARD_GEAR) {
throw new UnsupportedOperationException("不支持的升档操作...");
}
}
public void downShift() {
if (gearState == REVERSE_GEAR) {
throw new UnsupportedOperationException("不支持的降档操作...");
}
if (gearState == NEUTRAL_GEAR) {
gearState = REVERSE_GEAR;
System.out.println("从空档挂入倒车档...");
}
if (gearState == FIRST_FORWARD_GEAR) {
gearState = NEUTRAL_GEAR;
System.out.println("从1档挂入空档...");
}
if (gearState == SECOND_FORWARD_GEAR) {
gearState = FIRST_FORWARD_GEAR;
System.out.println("从2档挂入1档...");
}
if (gearState == THIRD_FORWARD_GEAR) {
gearState = SECOND_FORWARD_GEAR;
System.out.println("从3档挂入2档...");
}
if (gearState == FOURTH_FORWARD_GEAR) {
gearState = THIRD_FORWARD_GEAR;
System.out.println("从4档挂入3档...");
}
if (gearState == FIFTH_FORWARD_GEAR) {
gearState = FOURTH_FORWARD_GEAR;
System.out.println("从5档挂入4档...");
}
if (gearState == SIXTH_FORWARD_GEAR) {
gearState = FIFTH_FORWARD_GEAR;
System.out.println("从6档挂入5档...");
}
if (gearState == SEVENTH_FORWARD_GEAR) {
gearState = SIXTH_FORWARD_GEAR;
System.out.println("从7档挂入6档...");
}
}
}

代码之中充斥着大量的条件语句,看上去非常的不舒服。现在引入状态模式对原来结构进行调整。

将状态抽象出来

1
2
3
4
5
public abstract class GearState {
public abstract void doUpShift(GearShift gearShift);
public abstract void doDownShift(GearShift gearShift);
}

新的变速器类,Client只需要调用upShift操作和downShift操作就可以了,具体的状态交给GearState去处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GearShift {
public GearState gearState;
public GearShift() {
gearState = new NeutralGearState();
}
/*升档操作*/
public void upShift() {
gearState.doUpShift(this);
}
/*降档操作*/
public void downShift() {
gearState.doDownShift(this);
}
}

每个档位都抽成一个类并且继承GearState抽象类,代码太多,这里只选择空挡和一档进行展示

1
2
3
4
5
6
7
8
9
10
11
12
13
public class NeutralGearState extends GearState {
@Override
public void doUpShift(GearShift gearShift) {
gearShift.gearState = new FirstForwardGearState();
System.out.println("从空挡挂入一挡...");
}
@Override
public void doDownShift(GearShift gearShift) {
gearShift.gearState = new ReverseGearState();
System.out.println("从空挡挂入倒车挡...");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
public class FirstForwardGearState extends GearState {
@Override
public void doUpShift(GearShift gearShift) {
gearShift.gearState = new SecondForwardGearState();
System.out.println("从一挡挂入二挡...");
}
@Override
public void doDownShift(GearShift gearShift) {
gearShift.gearState = new NeutralGearState();
System.out.println("从一挡挂入空挡...");
}
}

这么一修改,代码的结构看上去整洁了许多了,而且扩展性也变好了。

总结

状态模式帮助我们更简单的控制对象的状态,结构变得更加简单易读了。状态模式和策略模式的结构看上去非常的相似,但是他们之间还是有区别的,策略模式的目的是为了通过不同的方法去完成一件事,而状态模式不一样,状态模式会根据当前的状态做出选择,比如开关灯的操作,用开关去操作时会去判断当前的开关状态再操作,如果灯关了就打开它,灯开了就关闭它。这也是GoF那本书里面讲的意图,在内部状态改变时改变自身的行为。

参考代码

------ 本文结束 ------

版权声明


BillyYccc's blog by Billy Yuan is licensed under a Creative Commons BY-NC-SA 4.0 International License.
本文原创于BillyYccc's Blog,转载请注明原作者及出处!