设计模式:状态(State)模式
设计模式:状态(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)模式的更多相关文章
- Java 实现状态(State)模式
/** * @author stone */ public class WindowState { private String stateValue; public WindowState(Stri ...
- 状态(State)模式--设计模式
定义与特点 1.1 定义 状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的类一样. 1.2 特点 状态模式优点: 封装了转换规则,并枚举可能的状态,它将所有与某个状 ...
- Head First 设计模式 —— 12. 状态 (State) 模式
思考题 public class GumballMachine { final static int SOLD_OUT = 0; final static int NO_QUARTER = 1; fi ...
- 状态(State)模式
状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的 ...
- 设计模式之State模式
State模式定义: 允许一个对象在状态改变是,改变它的行为.看起来对象似乎修改了它的类. 模式理解(个人): State模式主要解决的事在开发中时常遇到的根据不同状态需要进行不同的处理操作的问题,而 ...
- [设计模式]State模式
<Java与模式> 又称状态对象模式.状态模式是对象的行为模式.GOF95 一个对象的行为取决于一个或者多个动态变化的属性,这样的属性叫做状态.这样的对象叫做有状态的对象(stateful ...
- 设计模式C++描述----16.状态(State)模式
一. 举例 一般汽车发动机工作时有四种状态,吸气.压缩.做功和排气. 在运行时,不同的状态会有不同的行为,当前的状态机在适当的时候会过渡到下一状态. 其实用户在使用时根本不知道当前的状态,也无需知道当 ...
- C++设计模式之State模式
这里有两个例子: 1.https://www.cnblogs.com/wanggary/archive/2011/04/21/2024117.html 2.https://www.cnblogs.co ...
- 设计模式:state模式
核心: 把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化 例子: class State //状态接口 { public: virtual void show() = 0; ...
- Java设计模式(19)状态模式(State模式)
State的定义:不同的状态,不同的行为:或者说,每个状态有着相应的行为. 何时使用状态模式 State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If else ...
随机推荐
- c++ 常用的遍历,删除,分割等等文件处理函数代码实现
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9622851.html 删除文件目录函数: void myDeleteDirectory(CSt ...
- 【linux】在ubuntu中使用apt-get安装oracle jdk6
在Ubuntu 12.04 LTS上安装JDK6本身并不复杂,只是目前较新版本的Ubuntu已经不支持直接通过apt-get安装了.因此,需要从Oracle官方网站下载安装包进行安装. 从Oracle ...
- pow() 函数
pow() 函数用来求 x 的 y 次幂(次方),其原型为: double pow(double x, double y); #include<iostream> #include< ...
- Hibernate HQL多表查询
1.内连接和迫切内连接 (1)内连接 HQL语句:from 实体类名 实体类别名 inner join 实体类别名.表示另一个表数据的集合名称 (2)迫切内连接 HQL语句:from 实体类名 实体类 ...
- 持续集成工具TeamCity配置使用
持续集成CI(Continuous Integration)主要包括自动化的编译.发布和测试集成,对于我们信息系统项目开发非常有用.一般开发人员机器上会搭建自己的开发环境,整个项目在服务器上会搭建测试 ...
- Python爬虫-播报天气信息(生成exe文件)待续
#!/usr/bin/env python3 # -*- coding : utf-8 -*- '''1.从https://my.oschina.net/joanfen/blog/140364获取要播 ...
- 「Sdchr 的邀请赛」题解
骗个访问量.. A:取石子 将点 x 与点 x / prime 连边,那么这个图可以由指数之和的奇偶性来划分成一个二分图. 接下来考虑推广阶梯 NIM (或者这原本就是阶梯 NIM ?),必胜当且仅当 ...
- 改造一个JS插件的过程记录
最近做一个合作项目,对方要求我们做一个web应用程序,然后嵌入到他们的总的wen应用中,风格要求保持一致,于是乎就发了一个html文件过来,大概列举了一下各种控件,对话框的效果. 好了,重点说其中的一 ...
- MySQL 8.0 压缩包版安装方法
转自:https://blog.csdn.net/yangs_2012/article/details/80412016 注意: 操作系统:Windows 10 专业版(64位) MySQL版本:my ...
- JqueryEasyUI $.Parser
Parser(解析器) 对象的属性和方法: 使用: <link href="~/jquery-easyui-1.5.2/themes/bootstrap/easyui.css" ...