在面向对象编程中,是用类表示对象的。也就是说,程序的设计者需要考虑用类来表示什么东西。类对应的东西可能存在于真实世界中,也可能不存在于真实世界中。对于后者,可能有人看到代码后会感到吃惊:这些东西居然也可以是类啊。  

  在State模式中,用类来表示状态。用类来表示状态后,就能通过切换类方便地改变对象的状态,当需要增加新的状态时,如何修改代码这个问题也会很明确。

  示例程序的类图如上图所示。

 package bigjunoba.bjtu.state;

 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); // 在警报中心留下记录
}

  Context接口是负责管理状态和联系警报中心的接口。

package bigjunoba.bjtu.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); // 正常通话
}

  State接口是表示金库状态的接口。这些方法接收的参数Context是管理状态的接口。

 package bigjunoba.bjtu.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 "[白天]";
}
}

  DayState类表示白天的状态。对于每个表示状态的类,都只生成一个实例,因为如果每次发生状态改变时都生成一个实例的话,太浪费内存和时间了。因此,使用了Singleton模式。doClock方法是用于设置时间的方法。如果接收到的参数表示晚上的时间,就会切换到夜间状态,即发生状态变化,用程序表现就是获取夜晚状态的类的实例,然后通过changeState方法实现。

  

 package bigjunoba.bjtu.state;

 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 javax.swing.JButton; import java.awt.event.ActionEvent; public class SafeFrame extends Frame implements ActionListener, Context {
private TextField textClock = new TextField(60); // 显示当前时间
private TextArea textScreen = new TextArea(10, 60); // 显示警报中心的记录
private JButton buttonUse = new JButton("使用金库"); // 金库使用按钮
private JButton buttonAlarm = new JButton("按下警铃"); // 按下警铃按钮
private JButton buttonPhone = new JButton("正常通话"); // 正常通话按钮
private JButton buttonExit = new JButton("结束"); // 结束按钮 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");
}
}

  SafeFrame类是使用GUI实现警报系统界面的类。这里要注意的是,没有先去判断时间是白天还是晚上,也没有判断金库的状态,如果按下按钮就立即执行对应的方法。changeState方法会调用白天状态和晚上状态两个类,当状态发生迁移时,实际改变状态的是this.state = state;这句话就是给代表状态的字段赋予表示当前状态的类的实例,就相当于进行了状态迁移。

  上图为状态改变前后doUse方法的调用流程。一开始调用DayState类的doUse方法,当changeState后,变为了调用NightState类的doUse方法。

 package bigjunoba.bjtu.state;

 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) {
}
}
}
}
}

  Main类作为测试类,很容易理解,就不做解释了。

  实际效果图如上。

  State模式的类图如上图。

  State模式扩展知识:

  1.分而治之。在大规模的复杂处理时,不能用一般的方法解决时,会先将多个问题分解为多个小问题来解决。

  2.在State接口中声明的所有方法都是“依赖于状态的处理”,都是“状态不同处理也不同”。实现起来总结为:1.定义接口,声明抽象方法。 2.定义多个类,实现具体方法。

  3.实例的多面性

最新文章

  1. Xcode 常用快捷键
  2. acdreamoj1108(The kth number)
  3. eclipse 新建项目下后.metadata\.plugins的文件夹解释和如何保存自己的特定工程设置
  4. [Linux]XAMPP安装
  5. Android设计图(标注、切图)
  6. html5新特性--音频视频,拖放
  7. Java IO5:管道流、对象流
  8. 设计模式之观察者模式(Java)
  9. macbook连接linuxserver后不显示matlab桌面[问题]
  10. Setup a private http/nginx based GIT server
  11. 深入浅出数据结构C语言版(8)——后缀表达式、栈与四则运算计算器
  12. Loadrunner web_url函数学习(转贴)
  13. 六.ansible批量管理服务
  14. maven项目-修复Plugin execution not covered by lifecycle configuration: org.codehaus.mojo:build-helper-maven-plugin:1.8:add-resource (execution: add-resource, phase: generate-resources) pom.xml报错
  15. 266B
  16. Container&amp;injection
  17. 支持续传功能的ASP.NET WEB API文件下载服务
  18. 2019西湖论剑网络安全技能大赛(大学生组)--奇怪的TTL字段(补充)
  19. innerHTML、innerText和outerHTML、outerText的区别
  20. pandas操作速查表

热门文章

  1. CDH集群的配置优化须知
  2. Spring Boot Thymeleaf 实现国际化
  3. Spring boot 梳理 -@SpringBootApplication、@EnableAutoConfiguration与(@EnableWebMVC、WebMvcConfigurationSupport,WebMvcConfigurer和WebMvcConfigurationAdapter)
  4. unzip 命令指定解压路径
  5. 无法导入要素类到SDE中
  6. latex转word公式 java (latextoword,latex_word,latex2word,latex_omml)
  7. Kafka 学习笔记之 High Level Consumer相关参数
  8. ELK 学习笔记之 elasticsearch环境搭建
  9. php数字函数
  10. F#周报2019年第41期