背景
在State 模式中,我们用类表示状态。以类来表示状态后,我们就能通过切换类来方便地改变对象的状态
登场角色
State 状态
State 角色表示状态,定义了不同状态进行不同处理的接口
ConcreteState 具体的状态
ConcreteState 角色表示各个具体的状态,它实现了State接口
Context 状况,前后关系,上下文
Context 角色持有表示当前状态的 ConcreteState 角色,此外,他还定义了供外部调用者使用State 模式的接口
类图
示例代码
实例代码是模拟了一个金库报警系统,在白天和晚上的情况下,使用金库,打电话,使用警铃的方式会有所不同
State 接口
public interface State {
// 设置时间
void doClock(Context context, int hour);
// 使用金库
void doUse(Context context);
// 使用警报
void doAlarm(Context context);
// 使用电话
void doPhone(Context context);
}
DayState
public class DayState implements State {
private static DayState dayState = new DayState();
private DayState() {
}
public static DayState getInstance() {
return dayState;
}
@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("正常通话 [白天]");
}
}
NightState
public class NightState implements State {
private static NightState nightState = new NightState();
private NightState() {
}
public static NightState getInstance() {
return nightState;
}
@Override
public void doClock(Context context, int hour) { // 设置时间
if (!(hour < 9 || hour > 17)) {
context.changeState(DayState.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("晚上电话录音");
}
}
Context 接口
public interface Context {
// 设置时间
void setClock(int hour);
// 改变状态
void changeState(State state);
// 调用 警报中心
void callSecurityCenter(String msg);
// 记录日志
void recordLog(String msg);
}
SafeFrame
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) throws HeadlessException {
super(title);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
// 配置textClock
add(textClock, BorderLayout.NORTH);
textClock.setEditable(false);
// 配置 textScreen
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 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");
}
// 按钮被按下后该方法会被调用
@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("?");
}
}
}
功能分析
- 在state模式中,用类表示状态,并为每一种具体的状态都定义一个相应的类,可以替换复杂的 if-else;
- 使用Context 中的 一个state字段,就决定了系统的状态,不用定义过多的变量的值,不会存在自相矛盾的状态;
- 增加ConcreteState 角色是非常简单的,但是如果要增加“依赖于状态的处理”是非常困难的,因为要修改所有实现类中的方法;