状态模式

状态模式也称为状态机模式适用于当某个对象在它的状态发生改变时,其行为也随着发生比较大的变化,在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5

定义

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类,状态模式的核心是封装状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。

允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类,属于行为型模式。状态模式中类的行为是由状态決定的,不同的状态下有不同的行为,其意图是让一个对象在其内部改变的时候,其行为也随之改变。状态模式核心是状态行为绑定,不同的状态对应不同的行为。

实现

不使用状态模式

很明显在不适用状态模式时,通常会存在这种大量switch...caseif...else语句

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
public interface ILift {
// 电梯的4个状态
int OPENING_STATE = 1; //敞门状态
int CLOSING_STATE = 2; //闭门状态
int RUNNING_STATE = 3; //运行状态
int STOPPING_STATE = 4; //停止状态
// 设置电梯的状态
void setState(int state);
// 首先电梯门开启动作
void open();
// 电梯门可以开启, 那当然也就有关闭了
void close();
// 电梯要能上能下, 运行起来
void run();//电梯还要能停下来
void stop();
}
public class Lift implements ILift {
private int state;
public void setState(int state) {
this.state = state;
}
public void close() { // 电梯门关闭
//电梯在什么状态下才能关闭
switch (this.state) {
case OPENING_STATE: //可以关门, 同时修改电梯状态
this.closeWithoutLogic();
this.setState(CLOSING_STATE);
break;
case CLOSING_STATE: //电梯是关门状态, 则什么都不做
//do nothing;
break;
case RUNNING_STATE: //正在运行, 门本来就是关闭的, 也什么都不做
//do nothing;
break;
case STOPPING_STATE: //停止状态, 门也是关闭的, 什么也不做
//do nothing;
break;
}
}
public void open() { // 电梯门开启
//电梯在什么状态才能开启
switch (this.state) {
case OPENING_STATE: //闭门状态, 什么都不做
//do nothing;
break;
case CLOSING_STATE: //闭门状态, 则可以开启
this.openWithoutLogic();
this.setState(OPENING_STATE);
break;
case RUNNING_STATE: //运行状态, 则不能开门, 什么都不做
//do nothing;
break;
case STOPPING_STATE: //停止状态, 当然要开门了
this.openWithoutLogic();
this.setState(OPENING_STATE);
break;
}
}
public void run() { // 电梯开始运行起来
switch (this.state) {
case OPENING_STATE: //敞门状态, 什么都不做
//do nothing;
break;
case CLOSING_STATE: //闭门状态, 则可以运行
this.runWithoutLogic();
this.setState(RUNNING_STATE);
break;
case RUNNING_STATE: //运行状态, 则什么都不做
//do nothing;
break;
case STOPPING_STATE: //停止状态, 可以运行
this.runWithoutLogic();
this.setState(RUNNING_STATE);
}

}
//电梯停止
public void stop() {
switch (this.state) {
case OPENING_STATE: //敞门状态, 要先停下来的, 什么都不做
//do nothing;
break;
case CLOSING_STATE: //闭门状态, 则当然可以停止了
this.stopWithoutLogic();
this.setState(CLOSING_STATE);
break;
case RUNNING_STATE: //运行状态, 有运行当然那也就有停止了
this.stopWithoutLogic();
this.setState(CLOSING_STATE);
break;
case STOPPING_STATE: //停止状态, 什么都不做
//do nothing;
break;
}
}
//纯粹的电梯关门, 不考虑实际的逻辑
private void closeWithoutLogic() {
System.out.println("电梯门关闭...");
}
//纯粹的电梯开门, 不考虑任何条件
private void openWithoutLogic() {
System.out.println("电梯门开启...");
}
//纯粹的运行, 不考虑其他条件
private void runWithoutLogic() {
System.out.println("电梯上下运行起来...");
}
//单纯的停止, 不考虑其他条件
private void stopWithoutLogic() {
System.out.println("电梯停止了...");
}
}

状态模式主要包含三种角色:

  • 环境类角色(Context):定义客户端需要的接口,内部维护一个当前状态实例,并负责具体状态的切换
  • 抽象状态角色(State):定义该状态下的行为,可以有一个或多个行为
  • 具体状态角色(ConcreteState):具体实现该状态对应的行为,并且在需要的情况下进行状态切换
抽象状态角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class LiftState {
// 定义一个环境角色,也就是封装状态的变化引起的功能变化
protected Context context;
public void setContext(Context context) {
this.context = context;
}
// 电梯门开启动作
public abstract void open();
// 电梯门关闭动作
public abstract void close();
// 电梯运行动作
public abstract void run();
// 电梯停止动作
public abstract void stop();
}
具体状态角色
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
public class RunningState extends LiftState {
@Override
public void close() {
// do nothing
}
@Override
public void open() {
// do nothing
}
@Override
public void run() {
System.out.println("电梯上下运行...");
}
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState); // 环境设置为停止状态
super.context.getLiftState().stop();
}
}
public class StoppingState extends LiftState {
@Override
public void close() {
// do nothing;
}
@Override
public void open() {
super.context.setLiftState(Context.openningState);
super.context.getLiftState().open();
}
@Override
public void run() {
super.context.setLiftState(Context.runningState);
super.context.getLiftState().run();
}
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
public class OpenningState extends LiftState {
@Override
public void close() {
super.context.setLiftState(Context.closeingState); // 状态修改
super.context.getLiftState().close(); // 动作委托为CloseState来执行
}
@Override
public void open() {
System.out.println("电梯门开启...");
}
@Override
public void run() {
// 门开着时电梯不能运行,do nothing;
}
public void stop() {
//do nothing;
}
}
public class ClosingState extends LiftState {
@Override
public void close() {
System.out.println("电梯门关闭...");
}
@Override
public void open() {
super.context.setLiftState(Context.openningState); // 置为敞门状态
super.context.getLiftState().open();
}
@Override
public void run() {
super.context.setLiftState(Context.runningState); //设置为运行状态
super.context.getLiftState().run();
}
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState); //设置为停止状态
super.context.getLiftState().stop();
}
}
环境角色
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
public class Context {
// 定义出所有的电梯状态
public final static OpenningState openningState = new OpenningState();
public final static ClosingState closeingState = new ClosingState();
public final static RunningState runningState = new RunningState();
public final static StoppingState stoppingState = new StoppingState();
// 定义一个当前电梯状态
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
// 把当前的环境通知到各个实现类中
this.liftState.setContext(this);
}
public void open() {
this.liftState.open();
}
public void close() {
this.liftState.close();
}
public void run() {
this.liftState.run();
}
public void stop() {
this.liftState.stop();
}
}
Client调用
1
2
3
4
5
6
Context context = new Context();
context.setLiftState(new ClosingState());
context.open();
context.close();
context.run();
context.stop();

优点

  • 结构清晰:避免了过多的switch...caseif...else语句的使用,避免了程序的复杂性,提高系统的可维护性
  • 很好地体现了开闭原则单一职责原则,每个状态都是一个子类,状态类职责明确且具备扩展性
  • 封装性非常好,状态变换放置到类内部实现,外部调用不用知道类内部如何实现状态和行为的变换
  • 将状态转换显示化:通常对象内部是使用数值类型或枚举类型来定义状态,状态切换是通过赋值进行表现,不够直观;而使用状态类,在切换状态时是以不同的类进行表示,转换目的更加明确

缺点

  • 类膨胀:若一个事物具备很多状态,则会造成状态类太多
  • 状态模式的结构与实现都较为复杂,若使用不当将导致程序结构和代码的混乱
  • 状态模式对开闭原则的支持并不太好,对于可切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新増状态,且修改某个状态类的行为也需修改对应类的源代码

应用

状态模式主要解決的就是当控制一个对象状态的条件表达式过于复杂时的情況,通过把状态的判断逻辑转移到表示不同状态的一系列类中,可把复杂的判断逻辑简化。对象的行为依赖于它的状态,且会根据它的状态改变而改变它的相关行为。

  • 行为随状态改变而改变的场景
  • 条件分支判断语句的替代者

扩展