设计模式(十九)State模式
在面向对象编程中,是用类表示对象的。也就是说,程序的设计者需要考虑用类来表示什么东西。类对应的东西可能存在于真实世界中,也可能不存在于真实世界中。对于后者,可能有人看到代码后会感到吃惊:这些东西居然也可以是类啊。
在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.实例的多面性

设计模式(十九)State模式的更多相关文章
- 设计模式 ( 十九 ) 模板方法模式Template method(类行为型)
设计模式 ( 十九 ) 模板方法模式Template method(类行为型) 1.概述 在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行 ...
- 【转】设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成 ...
- 设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕 ...
- 设计模式 ( 十四 ) 迭代器模式Iterator(对象行为型)
设计模式 ( 十四 ) 迭代器模式Iterator(对象行为型) 1.概述 类中的面向对象编程封装应用逻辑.类,就是实例化的对象,每个单独的对象都有一个特定的身份和状态.单独的对象是一种组织代码的 ...
- C#设计模式之十九状态模式(State Pattern)【行为型】
一.引言 今天我们开始讲"行为型"设计模式的第六个模式,该模式是[状态模式],英文名称是:State Pattern.无论是现实世界,还是面向对象的OO世界,里面都有一个东西, ...
- C#设计模式之十九策略模式(Stragety Pattern)【行为型】
一.引言 今天我们开始讲“行为型”设计模式的第七个模式,该模式是[策略模式],英文名称是:Stragety Pattern.在现实生活中,策略模式的例子也非常常见,例如,在一个公司中,会有各种工作人员 ...
- Java设计模式十九——责任链模式
责任链模式 老李的苦恼 每个人在出生的时候,都早已在暗中被标好了三六九等. 老李是一名建筑工地的木匠,和大多数生活在社会最底层的农民工一样,一辈子老实本分,胆小怕事.在他们的心中,谁当老爷都没有区别, ...
- Java进阶篇设计模式之九----- 解释器模式和迭代器模式
前言 在上一篇中我们学习了行为型模式的责任链模式(Chain of Responsibility Pattern)和命令模式(Command Pattern).本篇则来学习下行为型模式的两个模式, 解 ...
- C#设计模式之九组合模式(Composite Pattern)【结构型】
一.引言 今天我们要讲[结构型]设计模式的第四个模式,该模式是[组合模式],英文名称是:Composite Pattern.当我们谈到这个模式的时候,有一个物件和这个模式很像,也符合这个模式要表达的意 ...
- Java设计模式之九 ----- 解释器模式和迭代器模式
前言 在上一篇中我们学习了行为型模式的责任链模式(Chain of Responsibility Pattern)和命令模式(Command Pattern).本篇则来学习下行为型模式的两个模式, 解 ...
随机推荐
- Linux 常用解压和压缩命令
.tar 解包 tar xvf filename.tar.tar 打包 tar cvf filename.tar dirname.gz 解压1 gunzip filename.gz.gz 解压2 gz ...
- JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!
为什么单线程是一个限制? 在发布的第一篇文章中,思考了这样一个问题:当调用堆栈中有函数调用需要花费大量时间来处理时会发生什么? 例如,假设在浏览器中运行一个复杂的图像转换算法. 当调用堆栈有函数要执行 ...
- 使用Hugo,只需5分钟,轻松搭建一个自己的博客
前面跟大家介绍过hexo这款静态博客系统,功能强大,基本能满足博客的各种需求.今天,我再跟大家介绍一款优秀的静态博客系统,那就是Hugo. Hugo是由Go语言实现的静态网站生成器.简单.易用.高效. ...
- [Week 2][Guarantee of PLA] the Correctness Verification of PLA
Conditions: For the data set D, there exists a $\displaystyle W_{f}$ which satisfies that for every ...
- forEach标签
1.forEach标签的简单使用: (1)未设置步长属性时,默认步长为1: <c:forEach "> <c:out value="${number}" ...
- SpringBootSecurity学习(15)前后端分离版之 OAuth2.0简单示例
OAuth2.0 OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者.客户端来申请资源,资源所有者同意以后,资源服务器可以向客户端颁发令牌.客户端通过令牌,去请求数据.也就是说, ...
- 从React-Native坑中爬出,我记下了这些
吐槽 如果React-Native是个人,我估计已经想要打死他了... 上一篇文章 当React开发者初次走进React-Native的世界 前言 最近因为业务需要,做了一些关于React-Nativ ...
- 品Spring:真没想到,三十步才能完成一个bean实例的创建
在容器启动快完成时,会把所有的单例bean进行实例化,也可以叫做预先实例化. 这样做的好处之一是,可以及早地发现问题,及早的抛出异常,及早地解决掉. 本文就来看下整个的实例化过程.其实还是比较繁琐的. ...
- IDEA 学习笔记之 Python项目开发
Python项目开发: 下载Python: https://www.python.org/downloads/release/python-363/ 安装Python: 配置环境变量(path): C ...
- else块的用途
除了在if...else...中使用,else块还可以在for循环.while循环以及try...except中使用. 在for循环中使用: my_list = ['a','b','c','d'] f ...