一、了解状态模式

1.1 什么是状态模式

状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它自己的类。

状态模式将状态封装为独立的类,并将动作委托到代表当前状态的对象。

1.2 状态模式组成结构

  • 上下文 (Context):用户对象,拥有(聚合)一个 State 类型的成员,以标识对象的当前状态。
  • 抽象状态 (State):接口或基类,封装与 Context 的特定状态相关的行为。
  • 具体状态 (ConcreteState):接口实现类或子类,实现了一个与 Context 某个状态相关的行为。

1.3 状态模式 UML 图解

1.4 状态模式与策略模式

如果对策略模式也了解的话,你会发现状态模式的类图与策略模式的类图几乎是一样的。它们之间有什么关系吗?
PS:策略模式具体博文http://blog.csdn.net/codejas/article/details/79077186

虽然状态模式与策略模式的类图是一样的,但是它们两个的“意图”是明显不一样的。

对于状态模式而言,我们将一群行为封装在状态对象中,context 的行为随时可委托到那些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映出 context 内部的状态,因此,context 的行为也会随着改变。但是 context 的客户对于状态对象的了解程度不多,甚至根本是浑然不知。

而对于策略模式而言,客户通常主动指定 context 所需要的策略对象是哪一个。策略模式虽然可以让我们的代码更有弹性,但是对于某个 context 对象来说,通常都只有一个最适当的策略对象。

1.5 状态模式应用场景

  • 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
  • 一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。

二、状态模式具体应用

2.1 问题描述

糖果机:一种新型糖果机的实现控制流程如图所示,现在需要我们用 Java 语言来实现它。

2.2 问题分析

从上面这个图中我们可以找出所有的状态:没有一块钱、有一块钱、售出糖果盒糖果告罄。也就是上图中的圆圈部分。为了提高程序的扩展性和维护性,我们采用状态模式来设计代码。将四个状态映射为对应的四个类。

我们需要做的事情如下:

  1. 首先定义一个 State 接口。在这个接口内,糖果机的每个动作都有一个对应的方法。
  2. 然后为机器中的每个状态实现状态类。这些类将负责在对应的状态下为糖果机工作。
  3. 将动作委托到状态类。

2.3 状态模式设计类图

2.4 代码实现

上下文糖果机 CandyMachine 类

package com.jas.state;/*** 糖果机类* @author Jas* @create 2018-01-31 10:35**/
public class CandyMachine {State noCoinState;State hasCoinState;State soldState;State soldOutState;State state = soldOutState;/** 刚开始糖果机中是没有糖果的,所以 count 为 0 */int count  = 0;    /*** 通过构造函数实例化所有状态对象* @param numberCandys 初始化糖果机中的糖果数量*/public CandyMachine(int numberCandys){noCoinState = new NoCoinState(this);hasCoinState = new HasCoinState(this);soldState = new SoldState(this);soldOutState = new SoldOutState(this);this.count = numberCandys;//如果超过 0 颗糖果,就把状态设为 noCoinStateif(numberCandys > 0){state = noCoinState;}}public void insertCoin(){state.insertCoin();}public void ejectCoin(){state.ejectCoin();}public void turnCrank(){state.turnCrank();state.dispense();}/*** 该方法用于释放糖果,并将 count 的数量减一*/void releaseBall(){System.out.println("糖果机释放一个糖果 ...");if (count != 0){count --;}}public State getNoCoinState() {return noCoinState;}public State getHasCoinState() {return hasCoinState;}public State getSoldState() {return soldState;}public State getSoldOutState() {return soldOutState;}public int getCount() {return count;}public void setState(State state){this.state = state;}
}

抽象状态 State 接口

package com.jas.state;/*** 所有的状态接口* @author Jas* @create 2018-01-31 10:28**/
public interface State {/** 投入一个硬币 */void insertCoin();/** 弹出硬币 */void ejectCoin();/** 转动曲柄 */void turnCrank();/** 糖果分发或告罄 */void dispense();
}

具体状态 NoCoinState 类

package com.jas.state;/*** @author Jas* @create 2018-01-31 10:30**/
public class NoCoinState implements State {CandyMachine candyMachine;public  NoCoinState(CandyMachine candyMachine){this.candyMachine = candyMachine;}/*** 投入硬币后,将糖果机置为有一个硬币的状态*/@Overridepublic void insertCoin() {System.out.println("你投入了一个硬币!");candyMachine.setState(candyMachine.getHasCoinState());}@Overridepublic void ejectCoin() {System.out.println("你还没有投入一个硬币!");}@Overridepublic void turnCrank() {System.out.println("即使你转动了曲柄,但是不会出糖果,因为你没有投入硬币!");}@Overridepublic void dispense() {System.out.println("你需要先投入一个硬币!");}
}

具体状态 HasCoinState 类

package com.jas.state;/*** @author Jas* @create 2018-01-31 10:39**/
public class HasCoinState implements  State {CandyMachine candyMachine;public HasCoinState(CandyMachine candyMachine){this.candyMachine = candyMachine;}@Overridepublic void insertCoin() {System.out.println("你已经投入了一个硬币,不能再投入硬币了!");}/*** 在糖果机有一个硬币的时候,你可以申请退回你的硬币,并改变糖果机的状态*/@Overridepublic void ejectCoin() {System.out.println("您的硬币已退回!");candyMachine.setState(candyMachine.getNoCoinState());}/*** 糖果机有硬币的时候,你可以转动曲柄,从而获得糖果,并改变糖果机的状态*/@Overridepublic void turnCrank() {System.out.println("你转动了曲柄,请稍等 ...");candyMachine.setState(candyMachine.getSoldState());}@Overridepublic void dispense() {System.out.println("现在还没有糖果发放!");}
}

具体状态 SoldState 类

package com.jas.state;/*** @author Jas* @create 2018-01-31 10:40**/
public class SoldState implements State {CandyMachine candyMachine;public SoldState(CandyMachine candyMachine){this.candyMachine = candyMachine;}@Overridepublic void insertCoin() {System.out.println("现在不能投入硬币了,请稍等,马上为您发放一个糖果!");}@Overridepublic void ejectCoin() {System.out.println("您已经转动了曲柄,现在不能再退回硬币了!");}@Overridepublic void turnCrank() {System.out.println("您已经转动了曲柄一次,多余的转动是无效的!");}/*** 分发糖果,并根据糖果机中的糖果数量,改变糖果机的状态*/@Overridepublic void dispense() {candyMachine.releaseBall();//如果糖果机中有糖果,将糖果机置为初始没有硬币的状态if(candyMachine.getCount() > 0){candyMachine.setState(candyMachine.getNoCoinState());}else {System.out.println("糖果已经售卖完了,请改天再来!");candyMachine.setState(candyMachine.getSoldOutState());}}
}

具体状态 SoldOutState 类

package com.jas.state;/*** @author Jas* @create 2018-01-31 10:41**/
public class SoldOutState implements State {CandyMachine candyMachine;public SoldOutState(CandyMachine candyMachine){this.candyMachine = candyMachine;}@Overridepublic void insertCoin() {System.out.println("请不要再投入硬币了,糖果已经卖完了!");}@Overridepublic void ejectCoin() {System.out.println("您没有投入硬币,所以现在不能弹出硬币!");}@Overridepublic void turnCrank() {System.out.println("即使你转动了曲柄,但还是不会出糖果,因为已经没有糖果了!");}@Overridepublic void dispense() {System.out.println("糖果已经卖完了,没有糖果用于发放了!");}
}

测试类

package com.jas.state;/*** @author Jas* @create 2018-01-31 11:04**/
public class CansyMachineTestDrive {public static void main(String[] args) {//初始化糖果机中的糖果数量为 1 个CandyMachine candyMachine = new CandyMachine(1);    candyMachine.insertCoin();candyMachine.ejectCoin();System.out.println("======================");candyMachine.insertCoin();candyMachine.turnCrank();System.out.println("======================");candyMachine.insertCoin();candyMachine.turnCrank();}
}/*** 输出* 你投入了一个硬币!* 您的硬币已退回!* ======================* 你投入了一个硬币!* 你转动了曲柄,请稍等 ...* 糖果机释放一个糖果 ...* 糖果已经售卖完了,请改天再来!* ======================* 请不要再投入硬币了,糖果已经卖完了!* 即使你转动了曲柄,但还是不会出糖果,因为已经没有糖果了!* 糖果已经卖完了,没有糖果用于发放了!*/

2.5 问题总结

在上面这个例子中,客户并不直接与具体状态类直接交互,而是通过糖果机类向状态类发出请求,还有要注意的是,客户并不会直接改变糖果机的状态。了解状态的是糖果机类,客户并不了解,所以客户不会直接与状态类交互。在糖果机类中有一个状态实例,用这个实例来完成状态之间不同的切换。

三、 状态模式总结

3.1 状态模式的优缺点

优点

  • 封装了转换规则。
  • 枚举可能的状态,在枚举状态之前需要确定状态种类。
  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点

  1. 状态模式的使用必然会增加系统类和对象的个数。
  2. 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  3. 状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

3.2 状态模式知识总结

  • 状态模式允许一个对象基于内部的状态而拥有不同的行为。
  • 状态模式用类来表示状态。
  • 上下文 (Context) 会将行为委托给当前的状态对象。
  • 状态模式和策略模式虽然有相同的类图,但是它们的意图是不一样的。
  • 状态模式允许上下文随着状态的改变而改变行为。
  • 使用状态模式通常会导致类的数量大量增加。
  • 状态类可以被多个上下文 (Context) 实例共享。

PS:点击了解更多设计模式 http://blog.csdn.net/codejas/article/details/79236013

参考文献

《Head First 设计模式》
http://www.runoob.com/design-pattern/state-pattern.html

Java 设计模式之状态模式相关推荐

  1. java设计模式之状态模式_Java中的状态设计模式

    java设计模式之状态模式 在本教程中,我们将探讨另一种流行的行为设计模式-状态设计模式. 当我们使用可以存在于多个状态的对象时,状态设计模式的知识变得非常有用. 当对象的行为取决于其当前状态时,我们 ...

  2. 图解java多线程设计模式 pdf_图解Java设计模式之状态模式

    图解Java设计模式之状态模式 APP抽象活动问题 状态模式基本介绍 状态模式的原理类图 状态模式解决APP抽奖问题 状态模式的注意事项和细节 APP抽象活动问题 请编写程序完成APP抽象活动,具体要 ...

  3. java状态模式例子答案_[转载]java设计模式_状态模式(带例子)

    状态模式(STATE) Java深入到一定程度,就不可避免的碰到设计模式这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广泛,遵循一定的编 ...

  4. java设计模式_状态模式

    2019独角兽企业重金招聘Python工程师标准>>> 状态模式类比if,else,状态决定行为.如果需要增加一个行为,通过状态模式,仅仅需要增加一个实现公共接口State的实现类即 ...

  5. 【状态模式】Java设计模式之状态模式

    何时使用 State模式在实际使用中比较多,适合"状态"的切换.因为我们经常会使用If else if else 进行状态切换,如果针对状态的这样判断切换反复出现,我们就要联想到是 ...

  6. Java设计模式之状态模式

    本文继续介绍23种设计模式系列之策略模式. 何时使用 State模式在实际使用中比较多,适合"状态"的切换.因为我们经常会使用If else if else 进行状态切换,如果针对 ...

  7. 【Java设计模式】状态模式

    ? 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的( stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的.当一个这样的对象与 ...

  8. Java设计模式之策略模式与状态模式

    一.策略模式定义 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们之间可以相互替换,策略模式可以在不影响客户端的情况下发生变化. 好了,定义看看就完了,我知道你很烦看定义. 二.策 ...

  9. java备忘录模式应用场景_图解Java设计模式之备忘录模式

    图解Java设计模式之备忘录模式 游戏角色状态恢复问题 游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态. ...

最新文章

  1. Scrapy框架--使用cookie
  2. 下载参考序列后字母转换小写转大写
  3. 中国人工智能学会通讯——人工智能如何造福人类 1.1 人工智能是中性技术
  4. legend3---lavarel多对多模型操作实例
  5. 透过认知智能剖析商业本质(iPIN CEO杨洋)丨硬创公开课
  6. 【Ping】unix - ping命令的退出状态
  7. 20220129刷题--第4题-- cookie
  8. mysql的相关技术说明_MySQL 系统架构 说明
  9. 明天发布的iPhone 11,只剩下价格是悬念了?
  10. zuul转发服务一直报404_SpringCloud之Zuul的多个使用场景
  11. 机器学习 之 LBP特征
  12. 海康摄像头故障处理踩坑记录
  13. My project
  14. iis无法启动万维网发布服务W3SVC
  15. 科罗拉多州立大学计算机科学,2019上海软科世界一流学科排名计算机科学与工程专业排名科罗拉多州立大学排名第401-500...
  16. 电气阀门定位器调试方法
  17. 灰狼(GWO)算法(附完整Matlab代码,可直接复制)
  18. 2015 UESTC 数据结构专题A题 秋实大哥与小朋友 线段树 区间更新,单点查询,离散化...
  19. 获取QQ音乐歌曲播放源地址
  20. 谷歌和百度的精确搜索技巧

热门文章

  1. 【大白话系列】深入浅出Cleaner+虚引用完成堆外内存的回收
  2. CTF Geek Challenge——第十一届极客大挑战Misc Write Up
  3. 中国科学院计算机专业职称,2018年春季工程技术系列专业技术资格职称评审结束...
  4. Python实训day01pm【练习题、文件编写、列表的使用】
  5. Retrofit 注解参数详解
  6. 如何设置eclipse下查看java源码
  7. 编程之美——4.11 扫雷游戏的概率
  8. 《java 进阶之路》 上--推荐书籍
  9. oracle流复制实现,Oracle流复制技术的基本概念、工作流程及其容灾备份
  10. 大工17春计算机应用基础在线测试2答案,大工14春《计算机应用基础》在线测试2...