根据不同状态对同一行为做出不同的处理。就是比如白天和晚上是两个不同状态(时间),吃饭是一个行为,早上喝粥和晚上吃水果是对这个行为的不同处理。
如果要在代码中实现这个操作,就会有很多的if 和 else ,然后执行不同的处理。
状态模式呢,就是将状态和行为的处理整合起来,从业务分离出去。比如我们可以把白天和晚上作为两个状态类,里面的方法就是分别实现不同的吃饭处理,然后在上下文中只保留一个当前状态的类。
示例程序是模拟金库的行为,在金库中有4个基本方法,setClock设置当前时间,changeState改变状态,callSecurityCenter求救,recordLog留言
Context接口,负责管理状态和联系警报中心的接口。
public interface Context {
void setClock(int hour);
void changeState(State state);
void callSecurityCenter(String msg);
void recordLog(String msg);
}
SafeFrame类,使用GUI实现警报系统界面的类,可以把它认为就是金库,实现了Context接口。
并且内部保存了一个状态对象,把金库想要根据不同状态所做出的处理,分散到各个状态的实现中(比如白天给报警中心打电话就是正常通话,晚上打电话就是留言)。
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("use");
private Button buttonAlarm = new Button("push");
private Button buttonPhone = new Button("call");
private Button buttonExit = new Button("end");
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 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");
}
}
State接口,表示金库状态的接口,在接口中定义了金库行为的对应方法,用以实现不同状态的不同处理。
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类,表示白天的状态,单例模式实现,并且会根据时间(hour)帮助调用金库changeState方法,来改变状态。
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 || 17 <= hour) {
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类,表示晚上的状态,与白天状态一样实现了State接口,但是提供了晚上状态时不同行为的具体实现。
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 && 17 > hour) {
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 "[ 晚上 ]";
}
}
