设计模式:状态(State)模式

一、前言

    状态模式在某些场合中使用是非常方便的,什么叫做状态,如果大家学过《编译原理》就会明白DFA M和NFA M,在确定有限状态机和非确定有限状态机中,状态就是最小的单元,当满足某种条件的时候,状态就会发生改变,我们可以把时间中的一个时刻当做一个状态,那么其实整个社会都是有状态组成的,前一时刻到下一时刻,整个社会上的物质(空间)发生了什么样的变化,因此状态可以非常的大也可以非常的小,天气变化情况是状态,白天和黑夜也是状态,人的生活作息等等都是状态,因此状态无处不在。那么状态模式就是将一个状态看做一个类,这与以往我们对类的理解不一样,以往我们认为类是对对象的抽象,用来表示对象的,对象一般是具体的事物,而现在我们将状态这种非具体的看不见的但又真实存在的事物当做类描述的东西,这一点可能需要大家理解。

    那么为什么必须要状态模式,不用状态模式可以吗?当然可以,但是还是回到了代码的可维护性、可扩展性、可复用性这个层面上来考虑问题,比如我们本例的内容,考虑一个银行系统,可以用来取款、打电话、报警、记录这四种功能,但是考虑如下需求:在白天如果我们去取款是正常的,晚上取款就要发出警报;在白天打电话有人接,晚上打电话启动留言功能;白天和晚上按警铃都会报警。那么我们应该如何设计这个程序呢,当然我们可以对每一个动作(作为一个函数),在这个函数内部,我们进行判断是白天还是黑夜,然后根据具体的情况做出反应。这样当然是可以的,但是假如我们的状态(白天和黑夜)非常的多呢,比如将24小时分成24个时间段(24个状态),那么我们对于每一个函数就要判断24遍,这无疑是非常糟糕的代码,可读性非常的差,并且如果需求发生了改变,我们很难去修改代码(很容易出现错误),但是如果我们考虑将这些状态都作为一个类,在每一个类内部进行处理、判断和相应的切换,这样思路就非常的清晰,如果再增加一种状态,代码需要修改的地方会非常的少,对于状态非常多的情景来说非常的方便。

二、代码

Context 接口:
 package zyr.dp.state;

 public interface Context {

     public abstract void setClock(int hour);
public abstract void changeState(State state);
public abstract void callSecurity(String str);
public abstract void recordLog(String msg); }
SafeFrame 实现类:
 package zyr.dp.state;

 import java.awt.*;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; public class SafeFrame extends Frame implements Context,ActionListener { private static final long serialVersionUID = 1676660221139225498L; private Button btnUse=new Button("使用");
private Button btnAlarm=new Button("警铃");
private Button btnPhone=new Button("打电话");
private Button btnExit=new Button("退出"); private TextField tfClock=new TextField(60);
private TextArea taAlarm=new TextArea(10,60); private State state=DayState.getInstance(); public SafeFrame(String title){
super(title);
setBackground(Color.BLUE);
setLayout(new BorderLayout()); add(tfClock,BorderLayout.NORTH);
tfClock.setEditable(false);
add(taAlarm,BorderLayout.CENTER);
taAlarm.setEditable(false); Panel panel=new Panel();
panel.add(btnUse);
panel.add(btnAlarm);
panel.add(btnPhone);
panel.add(btnExit);
add(panel,BorderLayout.SOUTH); pack();
show(); btnUse.addActionListener(this);
btnAlarm.addActionListener(this);
btnPhone.addActionListener(this);
btnExit.addActionListener(this);
} public void setClock(int hour) {
tfClock.setText(hour<10 ? "现在时间是:" + "0"+hour : "现在时间是:" +hour);
state.doClock(this, hour);
} public void changeState(State state) {
System.out.println("从状态"+this.state+"转变到了"+state);
this.state=state;
} public void callSecurity(String str) {
taAlarm.append("Call..."+str+"\n");
} public void recordLog(String msg) {
taAlarm.append("record..."+msg+"\n");
} public void actionPerformed(ActionEvent e) {
if(e.getSource()==btnUse){
state.doUse(this);
}else if(e.getSource()==btnAlarm){
state.doAlarm(this);
}else if(e.getSource()==btnPhone){
state.doPhone(this);
}else if(e.getSource()==btnExit){
System.exit(0);
}else{
System.out.print("未预料错误!");
}
} }
State 接口:
 package zyr.dp.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); }

  NightState实现类:

 package zyr.dp.state;

 public class NightState implements State {

     private NightState(){

     }
private static NightState nightState=new NightState(); public static NightState getInstance() {
return nightState;
} public void doClock(Context context, int hour) {
if(hour>=6 && hour <18){
//白天
context.changeState(DayState.getInstance());
}
} public void doUse(Context context) {
context.callSecurity("晚上使用");
} public void doAlarm(Context context) {
context.callSecurity("晚上警铃");
} public void doPhone(Context context) {
context.recordLog("晚上打电话");
} }

  DayState实现类:

 package zyr.dp.state;

 public class DayState implements State {

     private DayState(){

     }
private static DayState dayState=new DayState(); public static DayState getInstance() {
return dayState;
} public void doClock(Context context, int hour) {
if(hour<6 || hour >=18){
//晚上
context.changeState(NightState.getInstance());
}
} public void doUse(Context context) {
context.callSecurity("白天使用");
} public void doAlarm(Context context) {
context.callSecurity("白天警铃");
} public void doPhone(Context context) {
context.recordLog("白天打电话");
}
}

  Main类:

 package zyr.dp.state;

 public class Main {

     public static void main(String[] args) {

         SafeFrame f=new SafeFrame("状态模式");
while(true){
for(int hour=1;hour<=24;hour++){
f.setClock(hour);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} } }

 运行结果:

三、总结

   可以看到状态模式的强大威力,是用最简洁的代码通过接口、抽象类、普通类、继承、委托、代理模式等方式,将状态抽象为类,然后通过控制状态的逻辑委托不同的状态去做不同的事情,对于每一个状态来说又再次委托控制状态的逻辑做出相应的动作和修改,这样看起来比较复杂,其实仔细阅读就会发现因为接口(抽象类)的原因,使得程序非常的简洁,各个状态分工明确,密切配合。

   但是状态模式也有一些缺点,正是因为各个状态密切配合,在一个状态之中要知道其他状态的对象,这就造成了一定的关联,状态与状态之间是一种紧耦合的关系,这是状态模式的一点缺点,针对于这一点,我们可以将状态迁移的代码统一交给SafeFrame来做,这样就要使用到了Mediator仲裁者模式了。

   使用单例的原因是如果一直创造新的对象会对内存产生浪费,因此单例即可。同样的使用状态模式通过接口使用state变量来表示相应的状态,不会产生混淆和矛盾,相比于使用多个变量来分区间表示状态来说是非常清晰简练的。State模式便于增加新的状态(也需要修改其他状态的状态迁移代码),不便于增加新的“依赖于状态的处理”,比如doAlarm等,因为一旦增加了,实现了State接口的所有状态都要增加该部分代码。

   同时我们也看到了实例的多面性,比如SafeFrame实例实现了ActionListener接口和Context接口,那么就可以将new SafeFrame()对象传入fun1(ActionListener a)和fun2(Context context)这两个方法之中,之后这两个方法对该对象的使用是不同的,权限也不一样,因此多接口就会产生多面性。状态模式其实是用了分而治之的思想,将不同的状态分开来讨论,抽取共同性,从而使问题变得简单。

  程序代码

设计模式:状态(State)模式的更多相关文章

  1. Java 实现状态(State)模式

    /** * @author stone */ public class WindowState { private String stateValue; public WindowState(Stri ...

  2. 状态(State)模式--设计模式

    定义与特点 1.1 定义 状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的类一样. 1.2 特点 状态模式优点: 封装了转换规则,并枚举可能的状态,它将所有与某个状 ...

  3. Head First 设计模式 —— 12. 状态 (State) 模式

    思考题 public class GumballMachine { final static int SOLD_OUT = 0; final static int NO_QUARTER = 1; fi ...

  4. 状态(State)模式

    状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的 ...

  5. 设计模式之State模式

    State模式定义: 允许一个对象在状态改变是,改变它的行为.看起来对象似乎修改了它的类. 模式理解(个人): State模式主要解决的事在开发中时常遇到的根据不同状态需要进行不同的处理操作的问题,而 ...

  6. [设计模式]State模式

    <Java与模式> 又称状态对象模式.状态模式是对象的行为模式.GOF95 一个对象的行为取决于一个或者多个动态变化的属性,这样的属性叫做状态.这样的对象叫做有状态的对象(stateful ...

  7. 设计模式C++描述----16.状态(State)模式

    一. 举例 一般汽车发动机工作时有四种状态,吸气.压缩.做功和排气. 在运行时,不同的状态会有不同的行为,当前的状态机在适当的时候会过渡到下一状态. 其实用户在使用时根本不知道当前的状态,也无需知道当 ...

  8. C++设计模式之State模式

    这里有两个例子: 1.https://www.cnblogs.com/wanggary/archive/2011/04/21/2024117.html 2.https://www.cnblogs.co ...

  9. 设计模式:state模式

    核心: 把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化 例子: class State //状态接口 { public: virtual void show() = 0; ...

  10. Java设计模式(19)状态模式(State模式)

    State的定义:不同的状态,不同的行为:或者说,每个状态有着相应的行为. 何时使用状态模式 State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If else ...

随机推荐

  1. vue与TypeScript集成配置最简教程

    https://blog.csdn.net/u014633852/article/details/73706459 https://segmentfault.com/a/119000001187808 ...

  2. HttpUtils 封装类

    作为一个web开发人员,对Http 请求,并不陌生.有时候,我们请求的时候,需要使用代码实现,一般情况,我们使用Apache Jakarta Common 下的子项目.的HttpClient. 可是我 ...

  3. Unity Github 项目收集

    http://gad.qq.com/article/detail/24048 重磅推荐: Github 热门 Unity Assets 查询:http://unitylist.com/browse 最 ...

  4. Linux下ffmpeg添加Facebook/transform代码块实现将全景视频的球模型转换成立方体模型

    Facebook事实上已开始在平台中支持360度全景视频的流播,但公司对此并不满足.其工程师更是基于锥体几何学设计出了一套全新的视频编码,号称最高能将全景视频的文件大小减少80%.(VR最新突破:全景 ...

  5. jvm内置锁synchronized不能被中断

    很久没看技术书籍了,今天看了一下<七周七并发模型>前面两章讲的java,写的还是有深度的.看到了一个有demo,说jvm内置锁synchronized是不能被中断的.照着书上写了个demo ...

  6. 1.JDBC基础

    JDBC全称Java Database Connectivity,即Java数据库连接.(以下以MySQL为例,使用MySQL语句) Sun公司提供了标准JDBC API接口,没有实现具体类.各个数据 ...

  7. flask 继承模版的基本使用

  8. 一个简单的JQuery自适应分页插件twbsPagination

    下载地址:http://esimakin.github.io/twbs-pagination/ 1 解决totalPages不更新的问题 (先移除然后重新加入DOM树中)在使用twbsPaginati ...

  9. null id in com.rocky.** entry 错误处理

    1. 概述 使用hibernate往mysql数据库插入记录出错如下 10:37:57,364 ERROR [AssertionFailure] an assertion failure occure ...

  10. sass变量

    sass变量用法 1.sass变量必须以$符开头,后面紧跟着变量名 2.变量值和变量名之间就需要使用冒号(:)分隔开(就像CSS属性设置一样) 3.如果值后面加上!default则表示默认值 默认变量 ...