在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。如人的情绪有高兴的时候和伤心的时候,不同的情绪有不同的行为,当然外界也会影响其情绪变化。

对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用 if-else 语句来做状态判断,再进行不同情况的处理。但当对象的状态很多时,程序会变得很复杂。而且增加新的状态要添加新的 if-else 语句,这违背了“开闭原则”,不利于程序的扩展。

以上问题如果采用“状态模式”就能很好地得到解决。状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,放到一系列的状态类当中,这样可以把原来复杂的逻辑判断简单化。

认识状态模式

状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换,当一个对象的内在状态改变时,允许改变其行为。

状态模式是一种对象行为型模式,其主要优点如下。

  1. 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  2. 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  3. 有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

状态模式的主要缺点如下。

  1. 状态模式的使用必然会增加系统的类与对象的个数。
  2. 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

原理类图与角色说明

角色说明

  • 环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为。

状态模式的使用场景

  • 行为随状态改变而改变的的场景,例如权限设计,人员对象权限不同执行相同的行为其结果也会不同。
  • 条件、分支判断语句替代者,通过扩展子类实现条件判断处理

注意:状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不 要超过5个。

应用实例

下面以App抽奖为例运用状态模式:

App抽奖的具体要求如下:

  1. 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10%
  2. 奖品数量固定,抽完就不能抽奖
  3. 活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完
  4. 活动的四个状态转换关系图如下图

抽奖类图

代码实现

创建环境角色(Context):RaffleActivity
//管理抽奖状态类
public class RaffleActivity {// state 表示活动当前的状态,是变化State state = null;// 奖品数量int count = 0;// 四个属性,表示四种状态State noRafflleState = new NoRaffleState(this);State canRaffleState = new CanRaffleState(this);State dispenseState = new DispenseState(this);State dispensOutState = new DispenseOutState(this);//构造器//1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)//2. 初始化奖品的数量public RaffleActivity(int count) {this.state = getNoRafflleState();this.count = count;}//扣分, 调用当前状态的 deductMoneypublic void debuctMoney() {state.deductMoney();}//抽奖public void raffle() {// 如果当前的状态是抽奖成功if (state.raffle()) {//领取奖品state.dispensePrize();}}public State getState() {return state;}public void setState(State state) {this.state = state;}//每领取一次奖品,count--public int getCount() {int curCount = count;count--;return curCount;}public void setCount(int count) {this.count = count;}public State getNoRafflleState() {return noRafflleState;}public void setNoRafflleState(State noRafflleState) {this.noRafflleState = noRafflleState;}public State getCanRaffleState() {return canRaffleState;}public void setCanRaffleState(State canRaffleState) {this.canRaffleState = canRaffleState;}public State getDispenseState() {return dispenseState;}public void setDispenseState(State dispenseState) {this.dispenseState = dispenseState;}public State getDispensOutState() {return dispensOutState;}public void setDispensOutState(State dispensOutState) {this.dispensOutState = dispensOutState;}
}
创建抽象状态角色(State)
public interface State {//减积分public void deductMoney();//是否中奖public boolean raffle();//分发奖品public void dispensePrize();
}
创建具体状态角色(Concrete State)
//可以抽奖状态
public class CanRaffleState implements State {RaffleActivity activity;public CanRaffleState(RaffleActivity activity) {this.activity = activity;}/*** 已经扣除积分。不能再扣*/@Overridepublic void deductMoney() {System.out.println("已经扣除过积分");}//可以抽奖, 抽完奖后,根据实际情况,改成新的状态@Overridepublic boolean raffle() {System.out.println("正在抽奖,请稍等!");Random r = new Random();int num = r.nextInt(10);//10%中奖机会if (num == 0) {//中奖,改变活动状态为发放奖品activity.setState(activity.getDispenseState());return true;} else {System.out.println("很遗憾没有抽中奖品!");// 改变状态为不能抽奖activity.setState(activity.getNoRafflleState());return false;}}/*** 不能发放产品*/@Overridepublic void dispensePrize() {System.out.println("没中奖,不能发放产品");}
}
//不能抽奖状态
public class NoRaffleState implements State {// 初始化时传入活动引用,扣除积分后改变其状态RaffleActivity activity;public NoRaffleState(RaffleActivity activity) {this.activity = activity;}// 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态@Overridepublic void deductMoney() {System.out.println("扣除 50 积分成功,您可以抽奖了");activity.setState(activity.getCanRaffleState());}// 当前状态不能抽奖@Overridepublic boolean raffle() {System.out.println("扣了积分才能抽奖喔!");return false;}// 当前状态不能发奖品@Overridepublic void dispensePrize() {System.out.println("不能发放奖品");}
}
//发放奖品状态
public class DispenseState implements State {RaffleActivity activity;public DispenseState(RaffleActivity activity) {this.activity = activity;}@Overridepublic void deductMoney() {System.out.println("不能扣除积分");}@Overridepublic boolean raffle() {System.out.println("不能抽奖");return false;}//发放奖品@Overridepublic void dispensePrize() {if (activity.getCount() > 0) {System.out.println("恭喜中奖了");//改变状态为不能抽奖activity.setState(activity.getNoRafflleState());} else {System.out.println("很遗憾,奖品发送完了");// 改变状态为奖品发送完毕, 后面我们就不可以抽奖activity.setState(activity.getDispensOutState());}}
}
//奖品领完状态
public class DispenseOutState implements State {RaffleActivity raffleActivity;public DispenseOutState(RaffleActivity raffleActivity) {this.raffleActivity = raffleActivity;}@Overridepublic void deductMoney() {System.out.println("奖品发送完了,请下次再参加");}@Overridepublic boolean raffle() {System.out.println("奖品发送完了,请下次再参加");return false;}@Overridepublic void dispensePrize() {System.out.println("奖品发送完了,请下次再参加");}
}
客户端测试
public class Client {public static void main(String[] args) {// 创建活动对象,奖品有 1 个奖品RaffleActivity activity = new RaffleActivity(1);// 我们连续抽 20 次奖for (int i = 0; i < 20; i++) {System.out.println("--------第" + (i + 1) + "次抽奖----------");// 参加抽奖,第一步点击扣除积分activity.debuctMoney();// 第二步抽奖activity.raffle();}}
}
程序运行结果
--------第1次抽奖----------
扣除 50 积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------第2次抽奖----------
扣除 50 积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾,奖品发送完了
--------第3次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第4次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第5次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第6次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第7次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第8次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第9次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第10次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第11次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第12次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第13次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第14次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第15次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第16次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第17次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第18次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第19次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加
--------第20次抽奖----------
奖品发送完了,请下次再参加
奖品发送完了,请下次再参加

设计模式之状态模式详解(State Pattern)相关推荐

  1. android 状态模式,Android编程设计模式之状态模式详解

    本文实例讲述了Android编程设计模式之状态模式.分享给大家供大家参考,具体如下: 一.介绍 状态模式中的行为是由状态来决定的,不同的状态下有不同的行为.状态模式和策略模式的结构几乎完全一样,但它们 ...

  2. 设计模式之状态模式详解

    1 概述 [例]通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态.每一种状态改变,都有可能要根据其他状态来更新处理.例如,如果电梯门现在处于运行时状态,就不能进行开门操作 ...

  3. java状态机设计模式_Java设计模式之状态模式详解

    (本文由言念小文原创,转载请注明出处) 在实际工作中经常遇到某个对象,处于不同的状态有不同行为逻辑.且状态之间可以相互迁移的业务场景,特别是在开发通信协议栈类软件中尤为多见.<设计模式之禅> ...

  4. 设计模式——状态模式详解

    0. 前言 写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇效哦 ...

  5. (二十一)状态模式详解(DOTA版) - 转

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. 本次LZ给各位介绍状态模式,之前在写设计模式的时候,引入了一些小故事,二十章职责连模式是故事版的最后一篇,之后还剩余四个设计模式,LZ ...

  6. 设计模式之门面模式详解

    设计模式之门面模式详解 文章目录 设计模式之门面模式详解 一.什么是门面模式 二.门面模式的应用场景 三.门面模式的角色组成 四.门面模式通用写法 五.门面模式在业务中的应用 六.门面模式优缺点 一. ...

  7. 设计模式之桥接模式详解

    设计模式之桥接模式详解 文章目录 设计模式之桥接模式详解 一.什么是桥接模式 二.桥接模式的应用场景 三.桥接模式的角色组成 四.桥接模式通用写法示例 五.桥接模式优缺点 一.什么是桥接模式 桥接模式 ...

  8. 设计模式之模板方法模式详解

    设计模式之模板方法模式详解 概述 在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的 ...

  9. 设计模式——模版方法模式详解(论沉迷LOL对学生的危害)

    0. 前言 写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇效哦 ...

最新文章

  1. 浅谈本地文件包含利用
  2. 一文快速入门分库分表中间件 Sharding-JDBC
  3. 刚接手的项目代码 怎么看_11.21号动态:音恋今天公告称团队刚接手这个项目没多久...
  4. python画折线图详解-利用python画折线图
  5. 手机浏览器页面知识收集
  6. 光流 | 基于Matlab实现Lucas-Kanade方法:方法2(附源代码)
  7. Runtime底层原理--动态方法解析、消息转发源码分析
  8. Three.js的绘制流程(三)----地形
  9. 简单模拟实现简单的当登录延时的效果
  10. 笔记_SQLite入门
  11. Java基础入门笔记-单根继承
  12. Java中如何判断一个字符串是否为数字
  13. T2 Funcin T1,out T2(T1 arg)
  14. hive高级数据类型
  15. Linux:shell变量功能和Bash shell的操作环境
  16. 从源码编译安装TensorFlow
  17. 计算机考试系统———全套视频
  18. chrome 内核的浏览器pdf 字体显示问题
  19. 计算机机房岗位管理制度,机房管理规定-机房管理制度.doc
  20. 电脑插入了U盘却读取不出来的方法

热门文章

  1. 用计算机娱乐教学反思,计算机教学反思
  2. linux查看IO情况
  3. 【web搜索】学习笔记-层次汇合聚类HAC算法
  4. 有几个ZUCC (20分)
  5. python 梦幻西游_GitHub - BestBurning/mhxy: tensorflow实践:梦幻西游人物弹窗识别
  6. GCC源代码分析(1): GCC中的树
  7. 免费获取所有股票5分钟级别及以上的交易数据
  8. 俄罗斯DST收购ICQ;苹果收购芯片提供商(每日关注20100428)
  9. 微型计算机原理与接口技术(周荷琴 冯焕清)第六版 课后习题答案 第二章(部分答案)
  10. iOS开发之加载、滑动翻阅大量图片优化解决方案