【图解设计模式】State模式

用类来表示状态。

示例

一个警戒状态每小时会改变一次的报警系统。

m5bY4O.png

m5bdvd.png

类图

mI8u7Q.png

时序图

mI8gBD.png

State接口

1
2
3
4
5
6
public interface State {
public abstract void doClock(Context context, int hour);
public abstract void doUse(Context context);
public abstract void doAlarm(Context context);
public abstract void doPhone(Context context);
}

DayState类

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
public class DayState implements State {
private static DayState singleton = new DayState();

private DayState() {}

public static DayState getInstance() {
return singleton;
}

@Override
public void doClock(Context context, int hour) {
if (hour < 9 || hour >= 17)
context.changeState(NightState.getInstance());
}

@Override
public void doUse(Context context) {
context.recordLog("使用金库(白天)");
}

@Override
public void doAlarm(Context context) {
context.callSecurityCenter("按下警铃(白天)");
}

@Override
public void doPhone(Context context) {
context.callSecurityCenter("正常通话(白天)");
}

@Override
public String toString() {
return "[白天]";
}
}

NightState类

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
public class NightState implements State {
private static NightState singleton = new NightState();

private NightState() {}

public static NightState getInstance() {
return singleton;
}

@Override
public void doClock(Context context, int hour) {
if (hour >= 9 && hour < 17)
context.changeState(DayState.getInstance());
}

@Override
public void doUse(Context context) {
context.callSecurityCenter("紧急:晚上使用金库!");
}

@Override
public void doAlarm(Context context) {
context.callSecurityCenter("按下警铃(晚上)");
}

@Override
public void doPhone(Context context) {
context.recordLog("晚上的通话录音");
}

@Override
public String toString() {
return "[晚上]";
}
}

Context接口

1
2
3
4
5
6
public interface Context {
public abstract void setClock(int hour);
public abstract void changeState(State state);
public abstract void callSecurityCenter(String msg);
public abstract void recordLog(String msg);
}

SafeFrame类

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
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class SafeFrame extends Frame implements ActionListener, Context {
private TextField textClock = new TextField(60);
private TextArea textScreen = new TextArea(10, 60);
private Button buttonUse = new Button("使用金库");
private Button buttonAlarm = new Button("按下警铃");
private Button buttonPhone = new Button("正常通话");
private Button buttonExit = new Button("结束");

private State state = DayState.getInstance();

public SafeFrame(String title) {
super(title);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
add(textClock, BorderLayout.NORTH);
textClock.setEditable(false);
add(textScreen, BorderLayout.CENTER);
textScreen.setEditable(false);
Panel panel = new Panel();
panel.add(buttonUse);
panel.add(buttonAlarm);
panel.add(buttonPhone);
panel.add(buttonExit);
add(panel, BorderLayout.SOUTH);

pack();
show();

buttonUse.addActionListener(this);
buttonAlarm.addActionListener(this);
buttonPhone.addActionListener(this);
buttonExit.addActionListener(this);
}

@Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.toString());

if (e.getSource() == buttonUse)
state.doUse(this);
else if (e.getSource() == buttonAlarm)
state.doAlarm(this);
else if (e.getSource() == buttonPhone)
state.doPhone(this);
else if (e.getSource() == buttonExit)
System.exit(0);
else
System.out.println("?");
}

@Override
public void setClock(int hour) {
String clockstring = "现在时间是";

if (hour < 10)
clockstring += "0" + hour + ":00";
else
clockstring += hour + ":00";

System.out.println(clockstring);
textClock.setText(clockstring);
state.doClock(this, hour);
}

@Override
public void changeState(State state) {
System.out.println("从" + this.state + "状态变为了" + state + "状态。");
this.state = state;
}

@Override
public void callSecurityCenter(String msg) {
textScreen.append("call! " + msg + "\n");
}

@Override
public void recordLog(String msg) {
textScreen.append("record ... " + msg + "\n");
}
}

Main类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {

public static void main(String[] args) {
SafeFrame frame = new SafeFrame("State Sample");

while (true) {
for (int hour = 0; hour < 24; hour++) {
frame.setClock(hour);

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
}

运行结果

mIOYY6.png

登场角色

State(状态)

State角色表示状态,定义了根据不同状态进行不同处理的接口(API)。该接口(API)是那些处理内容依赖于状态的方法的集合。在示例程序中,由State接口扮演此角色。

ConcreteState(具体状态)

ConcreteState角色表示各个具体的状态,它实现了State接口。在示例程序中,由DayState类和NightState类扮演此角色。

Context(状况、前后关系、上下文)

Context角色持有表示当前状态的ConcreteState角色。此外,它还定义了供外部调用者使用State模式的接口(API)。在示例程序中,由Context接口和SafeFrame类扮演此角色。

类图

mIxR5n.png