您的位置:首页 > 编程语言 > Java开发

设计模式(十九)-状态模式(State Pattern)——用类表示状态

2018-02-08 17:36 531 查看
在面向对象变成中,是用类表示对象的。
在状态模式(State Pattern)中,我们用类来表示状态。
State.java
package com.test.dp.State.Sample;

//表示金库状态的接口
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.java
package com.test.dp.State.Sample;

//表示“白天”状态的类。它实现了State接口
public class DayState implements State {
private static DayState singleton = new DayState();
private DayState() { // 构造函数的可见性是private
}
public static State getInstance() { // 获取唯一实例
return singleton;
}
public void doClock(Context context, int hour) { // 设置时间
if (hour < 9 || 17 <= hour) {
context.changeState(NightState.getInstance());
}
}
public void doUse(Context context) { // 使用金库
context.recordLog("使用金库(白天)");
}
public void doAlarm(Context context) { // 按下警铃
context.callSecurityCenter("按下警铃(白天)");
}
public void doPhone(Context context) { // 正常通话
context.callSecurityCenter("正常通话(白天)");
}
public String toString() { // 显示表示类的文字
return "[白天]";
}
}
NightState.java
package com.test.dp.State.Sample;

//表示“晚上”状态的类。它实现了State接口
public class NightState implements State {
private static NightState singleton = new NightState();
private NightState() { // 构造函数的可见性是private
}
public static State getInstance() { // 获取唯一实例
return singleton;
}
public void doClock(Context context, int hour) { // 设置时间
if (9 <= hour && hour < 17) {
context.changeState(DayState.getInstance());
}
}
public void doUse(Context context) { // 使用金库
context.callSecurityCenter("紧急:晚上使用金库!");
}
public void doAlarm(Context context) { // 按下警铃
context.callSecurityCenter("按下警铃(晚上)");
}
public void doPhone(Context context) { // 正常通话
context.recordLog("晚上的通话录音");
}
public String toString() { // 显示表示类的文字
return "[晚上]";
}
}
Context.java
package com.test.dp.State.Sample;

//表示管理金库状态,并与报警中心联系的接口
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.java
package com.test.dp.State.Sample;

import java.awt.Frame;
import java.awt.Label;
import java.awt.Color;
import java.awt.Button;
import java.awt.TextField;
import java.awt.TextArea;
import java.awt.Panel;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

//实现了Context接口。在它内部持有按钮和画面显示等UI信息
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());
// 配置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);
}
// 按钮被按下后该方法会被调用
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("?");
}
}
// 设置时间
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 changeState(State state) {
System.out.println("从" + this.state + "状態变为了" + state + "状态。");
this.state = state;
}
// 联系警报中心
public void callSecurityCenter(String msg) {
textScreen.append("call! " + msg + "\n");
}
// 在警报中心留下记录
public void recordLog(String msg) {
textScreen.append("record ... " + msg + "\n");
}
}
Main.java
package com.test.dp.State.Sample;

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) {
}
}
}
}
}
执行结果:

总结:

应用实例:1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。2、曾侯乙编钟中,'钟是抽象接口','钟A'等是具体状态,'曾侯乙编钟'是具体环境(Context)。
[b]优点:
1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点:1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景:1、行为随状态改变而改变的场景。2、条件、分支语句的代替者。
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息