设计模式第10式:状态模式
前言
我们遇到状态机模型,常常会理不清“状态”和“行为”的关系。状态模式就是专门解决这个应用场景的,它通过改变对象内部的状态来帮助对象控制自己的行为。
正文
1、先来看一个案例
我们选取《HeadFirst 设计模式》中的糖果售卖机案例。这就是常见的状态机模型。
首先我们找出所有的状态:上图中的4个圆圈。于是我们用4个常量来表示每个状态:
final static int SOLD_OUT = 0;
final static int NO_QUARTER = 1;
final static int HAS_QUARTER = 2;
final static int SOLD = 3;int state = SOLD_OUT; // 初始状态
然后我们将系统中可以发生的动作整理出来,每个动作都对应一个方法。下面这个方法就是投币动作对应的方法。方法里面对每一个可能出现的状态展示适当的行为。
public void insertQuarter() {if (state == HAS_QUARTER) {sout("不能再投币了");} else if (state == SOLD_OUT) {sout("卖完了");} else if (state == SOLD) {sout("已售出");} else if (state == NO_QUARTER) {state = HAS_QUARTER;sout("已投币");}
}public void ejectQuarter() {if (state == HAS_QUARTER) {...} else if (state == SOLD_OUT) {...} else if (state == SOLD) {...} else if (state == NO_QUARTER) {...}
}public void turnCrank() {if (state == HAS_QUARTER) {...} else if (state == SOLD_OUT) {...} else if (state == SOLD) {...} else if (state == NO_QUARTER) {...}
}public void dispense() {if (state == HAS_QUARTER) {...} else if (state == SOLD_OUT) {...} else if (state == SOLD) {...} else if (state == NO_QUARTER) {...}
}
其他的动作方法类似,里面都有一大堆if else代码。当我们费尽九牛二虎之力写完后,要加入新的需求:当转动糖果机摇杆,有10%的几率掉下来2颗糖果。这种情况下完全失控了,每一个动作方法都要跟着修改,改出BUG的几率会大大增加。
2、如何优化
仍然遵循“封装变化”的原则,我们将每个状态的行为都放在各自的类中,那么每个状态只需要实现它自己的动作就可以了。这样,糖果机只需要将动作委托给当前的状态对象。
- 首先,我们定义一个State接口,在这个接口中,糖果机的每个动作都有一个对应的方法;
- 然后,为机器中的每个状态实现一个状态类,这些类将负责在对应状态下进行机器的行为;
- 最后,我们将糖果机的动作委托给状态类。
我们尝试实现其中一个状态类,我们要做的是实现当前状态下恰当的行为:
public class NoQuarterState implements State {Machine machine;// 构造器拿到糖果机的引用public NoQuarterState(Machine machine) {this.machine = machine;}// 当前状态下触发动作的结果public void insertQuarter() {sout("已投币");// 触发糖果机改变状态machine.setState(machine.getHasQuarterState());}public void ejectQuarter() {sout("未投币");}public void turnCrank() {sout("未投币");}public void dispense() {sout("未投币");}
}
于是我们重新改造糖果机:
public class Machine {// 常量替换成4个状态类State soldOutState;State noQuarterState;State hasQuarterState;State soldState;State state = soldOutState; // 当前状态// 构造方法初始化每种状态public Machine() {soldOutState = new SoldOutState(this);noQuarterState= new NoQuarterState(this);hasQuarterState= new HasQuarterState(this);soldState = new SoldState(this);}// 糖果机的动作均委托给当前状态类的动作方法public void insertQuarter() {soldState.insertQuarter();}public void ejectQuarter() {soldState.ejectQuarter();}public void turnCrank() {soldState.turnCrank();}public void dispense() {soldState.dispense();}
}
3、定义状态模式
上面已经实现了状态模式,我们来对其进行一个定义:
状态模式允许对象在内部状态改变时改变它自身的行为,对象看起来好像修改了它的类。
- 这个模式将状态封装成独立的类,并将动作委托给当前状态对象
- 从用户的视角来看,如果使用的对象能否完全改变自己的行为,那么这个对象应该是从别的类实例化而来的。然而,状态模式仅通过组合不同的状态对象来造成改变类行为的假象。
我们来定义下状态模式的类图:
4、状态模式 vs 策略模式
两个模式的类图很相似,但是两个模式的行为不一样:策略模式替换算法是由用户自己控制的,状态模式替换算法是系统内部自行控制的。
总结
状态模式针对状态机场景有很好的模型总结,我们遇到状态机模型,都要首先考虑状态模式。
设计模式第10式:状态模式相关推荐
- 设计模式 with Python 10:状态模式
设计模式 with Python 10:状态模式 如果你接触过UML的状态图,应该会对状态图或者状态机有所了解,我们今天讨论的状态模式就是这种设计的落地方案. 和之前的讲解一样,我们从一个具体案例&q ...
- Java设计模式(10)代理模式(Proxy模式)
理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,P ...
- 《Head First 设计模式》学习笔记——状态模式
在软件开发过程中.应用程序可能会依据不同的情况作出不同的处理. 最直接的解决方式是将这些全部可能发生的情况全都考虑到.然后使用if... ellse语句来做状态推断来进行不同情况的处理. 可是对复杂状 ...
- 【设计模式系列19】状态模式原理分析及其和策略模式,责任链模式的区别
状态模式原理分析 设计模式系列总览 前言 什么是状态模式 状态模式示例 状态模式角色 状态模式与责任链模式 状态模式与策略模式 状态模式应用场景 状态模式优缺点 总结 设计模式系列总览 设计模式 飞机 ...
- 设计模式笔记 10.Facade 外观模式(结构型模式)
10. Facade 外观模式 2008-8-18 动机(Motivation) 上述A方案的问题在于组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种 ...
- 设计模式的理解:状态模式(State) 和备忘录模式(Memento)
一.状态模式 状态模式,允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类.它的实现方式和策略模式相似,目的都是对if...else语句进行优化,只不过,策略模式通过外部传入枚举.条 ...
- 设计模式21:State 状态模式(行为型模式)
State 状态模式(行为型模式) 动机(Motivation) 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态的行为就可能完全不同. ...
- 《Head First 设计模式》第十章-状态模式 状态模式
状态模式 策略模式和状态模式是双胞胎,在出生时才分开.你已经知道,策略模式是围绕可以互换的算法来创建成功业务的,然而,状态走的是更崇高的路,它通过改变对象内部的状态来帮助对象控制自己的行为. 定义状态 ...
- 设计模式学习笔记(6) - 状态模式
状态模式与策略模式很像,真的很像. 下面举个例子来说明,我们都知道银行经常将用户划分个三六九等,划分的方法很简单,就是用户的存款.下面用php代码来模拟下这个过程: <?php /** * 状 ...
最新文章
- js跟随鼠标移动的写法
- python结构嵌套_python2.3嵌套if结构:
- Java中的异常全面讲解
- 全局程序集缓存gac中安装程序集_我就不信2W字把源码拆的这么碎,你还不明白mybatis缓存...
- display:inline、block、inline-block的区别
- 在Spring Boot启动时运行代码
- TensorFlow保存和载入训练模型
- mac下使用mysql
- C# 图片处理之:旋转图片任意角度
- C. Memory and De-Evolution 逆向思维
- #pragma comment (lib, ws2_32.lib) 调用报错
- 一卡通管理系统服务器,一卡通管理系统
- 数据分析的坑,都在统计学里埋过
- R语言探索性因子分析练习
- 使用dkms将驱动加入内核模块
- SPSS(一)进行单因素方差分析及多重比较检验(图文教程)
- 10g recyclebin与用户表空间限额
- 晒一波程序员的桌面,逼格超级高!
- windows录屏_Windows电脑怎么录制屏幕?查看电脑自动录屏方法
- 设计模式GOF23之-------------------结构型模式(适配器模式、代理模式、桥接模式、装饰模式、组合模式、外观模式、享元模式)
热门文章
- 记录一次mongoDB错误 errmsg: cannot use the part () to traverse the element
- GPU、CPU、显卡区别
- C语言/C++编程学习:和QT零距离接触的意义
- Bootstrap的基本使用方法介绍
- JAVA 冷知识( break outer ;)
- MPAndroidChart的详细使用——BarChart条形图组(三)
- Linux内核全版本下载地址
- C语言中static与extern用法总结
- 在UE4里实现四叉树查找最近点
- Windows上CLion配置和使用教程