赞
踩
》》在 State 模式中,我们用类来表示状态。
以类来表示状态后,我们就能通过切换类来
方便地改变对象的状态。当需要增加新的状态时,
如何修改代码这个问题也会很明确。
------------------下面的示例程序:金库报警系统
(警戒状态每小时会改变一次的警报系统)
在我们即将要写的程序中,并不会真正呼叫警报中心,只是在页面上显示呼叫状态。此外,如果以现实世界中
的时间来测试程序就太慢了,所以我们假设程序中的 1 秒对应现实世界中的一个小时。
---------------------------金库警报系统的结构图:
--------------------------------《示例程序的运行结果》
------------------------------------------------------------------
《不使用 State 模式的伪代码 》
警报系统的类{
使用金库时被调用的方法(){
if (白天){
向警报中心报告使用记录
}else if (晚上){
向警报中心报告紧急事态
}
}
警铃响起时被调用的方法(){
向警报中心报告紧急事态
}
正常通话时被调用的方法(){
if(白天){
呼叫警报中心
}else if(晚上){
呼叫警报中心的留言电话
}
}
}
---------------------------------------------------------------------
《使用了 State 模式的伪代码》
表示白天的状态的类{
使用金库时被调用的方法(){
向警报中心报告使用记录
}
警铃响起时被调用的方法(){
向警报中心报告紧急事态
}
正常通话时被调用的方法(){
呼叫警报中心
}
}
>>>>>>
表示晚上的状态的类{
使用金库时被调用的方法(){
向警报中心报告紧急事态
}
警铃响起时被调用的方法(){
向警报中心报告紧急事态
}
正常通话时被调用的方法(){
呼叫警报中心的留言电话
}
}
备注:上面两种伪代码:
》》不使用 State 模式的伪代码:会先在各个方法里面使用 if 语句判断
现在是白天还是晚上,然后再进行相应的处理(用方法来表示
状态)
》》使用 State 模式的伪代码:用类来表示白天和晚上。
(用类来表示状态)
--------------------------下面的内容是使用了 State 模式的
》》示例程序的类图:
》》State 接口:
package 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 类:
package state;
/*
* 该类表示白天的状态,实现了 State 接口,实现了State 接口中声明的所有方法
*
* 对于每个表示状态的类,我们都只能生成一个实例。这里用了 单例模式
*/
public class DayState implements State {
private static DayState singleton = new DayState();
//构造函数的可见性是 private
private DayState(){
}
//获取唯一实例
public static State getInstance(){
return singleton;
}
//设置时间
/*
* 如果接收到的参数是表示晚上的时间,就会切换到夜间状态,即发生了状态变化(状态迁移)
*/
public void doClock(Context context, int hour) {
if(hour < 9 || hour >= 17){
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 类:
package state;
/*
* 该类的结构与 DayState类的结构 完全一样
*/
public class NightState implements State {
private static NightState singleton = new NightState();
//构造函数的可见性是 private
private NightState(){
}
//获取唯一实例
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 接口:
package 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); //在警报中心留下记录
}
》》SafeFrame 类:
package state;
/*
* 该类使用 GUI 实现了警报系统界面的类,它实现了 Context 接口。
* 该类中有表示文本输入框(TextField)、多行文本输入框(TextArea)和按钮(Button)
* 等各种控件的字段。
* 该类的构造函数进行了以下处理:(1)、设置背景色
* (2)、设置布局管理器
* (3)、设置控件
* (4)、设置监听器
* 监听器的设置非常重要。我们通过调用各个按钮的 addActionListener() 方法来设置监听器,
* addActionListener() 方法接收的参数是“当按钮被按下时会被调用的实例”,该实例必须
* 是实现了 ActionListener 接口的实例。
*/
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SafeFrame extends Frame implements Context, ActionListener {
//显示当前时间
private TextField textClock = new TextField(60);
//显示警报中心的记录
private TextArea textScreen = new TextArea(20 , 60);
//使用金库按钮
private Button buttonUse = new Button("use");
//按下警铃按钮
private Button buttonAlarm = new Button("alarm");
//正常通话按钮
private Button buttonPhone = new Button("phone");
//结束按钮
private Button buttonExit = new Button("exit");
//当前的状态
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();
setVisible(true);
//设置监听器
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 类 -----》测试程序
package state;
/*
* 该类是测试程序,生成了SafeFrame 的实例并每秒调用一次 setClock 方法对该实例
* 设置一次时间。
* 该类中使用了 线程的知识
*/
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){
}
}
}
}
}
---------------------------------------------------------------------
《State 模式中的登场角色》
**** State (状态)
State 角色表示状态,定义了根据不同状态进行不同处理的接口。该接口是那些处理
内容依赖于状态的方法的集合。在示例程序中,State 接口扮演此举色。
**** ConcreteState (具体的状态)
ConcreteState 角色表示各个具体的状态,它实现了 State 接口。在示例程序中,
由 DayState 类和 NightState 类扮演此角色。
**** Context (状况、前后关系、上下文)
Context 角色持有表示当前状态的 ConcreteState 角色。此外,它还定义了供外部
调用者使用 State 模式的接口。在示例程序中,由Context 和 SafeFrame 类扮演此角色。
---------------------------------------------------------------------
《扩展思路的要点》
1.分而治之
**分而治之,简单而言就是将一个复杂的大问题分解为多个小问题然后逐个解决。
**在 State 模式中用类来表示系统的“状态”,并以此将复杂的程序分解出来。
2.依赖于状态的处理
** 在 State 接口中声明的所有方法都是“依赖于状态的处理”,都是“状态不同处理也不同”。
** 在State 模式中,我们应该如何编程,以实现“依赖于状态的处理”呢?总结起来如下
两点:
(1)、定义接口,声明抽象方法
(2)、定义多个类,实现具体方法
3.应当是谁来管理状态迁移
** 在 State 模式中,需要注意的是:应当是谁来管理状态迁移。
** 除了上面示例程序中,处理状态迁移的方法外,还可以使用状态迁移表来设计程序。
所谓状态迁移表是可以根据“输入和内部状态”得到“输出和下一个状态”的一览表。
当状态迁移遵循一定的规则时,使用状态迁移表非常有效。
** 此外,当状态数过多时,可以用程序来生成代码而不是手写代码。
4.不会自相矛盾
** 如果不使用 State 模式,我们需要使用多个变量的值的集合来表示系统的状态。这时,
必须十分小心,注意不要让变量的值之间互相矛盾。
** 在 State 模式中,是用类来表示状态的。这样,我们就只需要一个表示系统状态的变量
即可。在示例程序中,SafeFrame 类的 state 字段就是这个变量,它决定了系统的状态。
因此,不会存在自相矛盾的状态。
5.易于增加新的状态
** 在 State 模式中,增加新的状态是非常简单的。
** 但是,在State 模式中增加其他“依赖于状态的处理”是很困难的。
(依赖于状态的处理-----》 即 方法)
6.实例的多面性
-------------------------------------------------------------------
《相关的设计模式》
*** Singleton 模式
*** Flyweight 模式
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。