Author:Gorit
Date:2021年11月25日
Refer:《图解设计模式》
19.1 State 模式
在 State 模式,我们将会用 “类”来表示状态。
现实世界中,我们会考虑各种东西的“状态”,但是不会讲“状态”当做东西看待
本章我们将会用 类 来表示状态,通过切换类来方便的改变对象的状态。当腰新增代码状态时,如何修改代码这个问题会变得明确。
State 模式中:
- 使用方法判断状态
- 用类来表示状态
19.2 示例程序
类名 | 说明 |
---|---|
State | 表示金库状态的接口 |
DayState | 表示“白天”状态的类,实现了 State 接口 |
NightState | 表示“晚上”状态的类,实现了State 接口 |
Context | 表示管理金库状态,并与警报中心联系的接口 |
SafeFrame | 实现 Context 接口, 在它内部持有 按钮 和 画面显示 UI 信息 |
Main | 测试程序行为的类 |
State
package State;
/**
* @Author Gorit
* @Date 2021/11/26
* 设置时间
* 使用金库
* 按下警铃
* 正常通话
**/
public interface State {
public abstract void doClock(Context context, int hour); // 设置时间
public abstract void doUse(Context context); // 使用金库
public abstract void doAlram(Context context); // 按下警铃
public abstract void doPhone(Context context); // 正常通话
}
DayState
package State;
/**
* @Author Gorit
* @Date 2021/11/26
**/
public class DayState implements State{
private static DayState singleton = new DayState();
private DayState() {}
// 单例模式获取唯一实例
public static State getInstance() {
return singleton;
}
public void doClock(Context context, int hour) {
if (hour < 9 || hour >= 17) {
context.changeState(NightState.getInstance());
}
}
public void doUse(Context context) {
context.recordLog("使用金库(白天)");
}
public void doAlram(Context context) {
context.callSecurityCenter("按下警铃(白天)");
}
public void doPhone(Context context) {
context.callSecurityCenter("正常通话(白天)");
}
public String toString() {
return "[白天]";
}
}
NightState
package State;
/**
* @Author Gorit
* @Date 2021/11/26
**/
public class NightState implements State {
private static NightState singleton = new NightState();
private NightState() {}
// 单例模式获取唯一实例
public static State getInstance() {
return singleton;
}
public void doClock(Context context, int hour) {
if (hour <= 9 && hour < 17) {
context.changeState(DayState.getInstance());
}
}
public void doUse(Context context) {
context.callSecurityCenter("紧急!晚上使用金库!");
}
public void doAlram(Context context) {
context.callSecurityCenter("按下警铃(晚上)");
}
public void doPhone(Context context) {
context.recordLog("晚上通话录音");
}
public String toString() {
return "[夜晚]";
}
}
Context
package State;
/**
* @Author Gorit
* @Date 2021/11/26
**/
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
package State;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @Author Gorit
* @Date 2021/11/26
* 设置背景色
* 设置布局管理器
* 设置控件
* 设置监听器(Listener)
**/
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 buttonAlert = new Button("按下警铃");
private Button buttonPhone = new Button("正常通话");
private Button buttonExit = new Button("结束");
private State state = DayState.getInstance(); // 当前状态
public SafeFrame(String title) throws HeadlessException {
super(title);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
// 配置 textClock
add(textClock, BorderLayout.NORTH);
textClock.setEditable(false);
// 配置 text Screen
add(textScreen, BorderLayout.CENTER);
// 为界面添加按钮
Panel panel = new Panel();
panel.add(buttonUse);
panel.add(buttonAlert);
panel.add(buttonPhone);
panel.add(buttonExit);
// 配置界面
add(panel, BorderLayout.SOUTH);
// 显示
pack();
setVisible(true);
// 设置监听器
buttonUse.addActionListener(this);
buttonAlert.addActionListener(this);
buttonPhone.addActionListener(this);
buttonExit.addActionListener(this);
}
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);
}
// 联系警报中心
public void callSecurityCenter(String msg) {
textScreen.append("call!!!" + msg + "\n");
}
// 警告中心留下记录
public void recordLog(String msg) {
textScreen.append("record...." + msg + "\n");
}
// 改变状态
public void changeState(State state) {
System.out.println("从" + this.state + "状态变为" + state + "状态");
this.state = state;
}
public void actionPerformed(ActionEvent e) {
System.out.println(e.toString());
if (e.getSource() == buttonUse) {
state.doUse(this); // 金库使用按钮
} else if (e.getSource() == buttonAlert) {
state.doAlram(this); // 按下报警按钮
} else if (e.getSource() == buttonPhone) {
state.doPhone(this); // 按下打电话
} else if (e.getSource() == buttonExit) {
System.exit(0); // 退出
} else {
System.out.println("?");
}
}
}
Main
package State;
/**
* @Author Gorit
* @Date 2021/11/26
**/
public class Main {
public static void main(String[] args) {
SafeFrame frame = new SafeFrame("State Simple");
while (true) {
for (int hour = 0; hour < 24; hour++) {
frame.setClock(hour);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
19.3 State 中登场的角色
一、State 状态
State 角色表示状态,定义了根据不同状态进行不同处理的接口(API)。
该接口是那些 处理内容依赖于状态的方法的集合
二、ConcreateState(具体状态)
ConcreateState 角色表示具体的状态,实现了 State 的接口。示例程序中由 DayState 和 NightState 类扮演此角色
三、Context(状况,前后关系,上下文)
Context 角色持有表示当前状态的 ConcreateState 角色。此外,它还定义了外部调用者使用 State 模式的接口(API)。
示例程序由 Context 和 SafeFrame 表示当前角色状态
本案例中:
- Context 接口定义了供外部调用者使用 State 模式的接口
- SafeFrame 类则持有表示当前状态的 ConcreateState 角色。
19.4 相关设计模式
一、Singleton 模式
Singleton 模式常常出现在 ConcreateState 角色中。生成唯一实例来表示当前状态
二、Flyweight 模式
在表示状态的类中没有定义任何实例字段。
因此,有时我们可以用 Flyweight 模式在多个 Context 角色之间共享 ConcreateState 角色。