内功心法专题-设计模式-18、状态模式
1. 模式定义
- 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换❕
- 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
- 对==有状态的对象==,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
2. 模式结构⭐️🔴
2.1. 模式角色
- 环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为。
❕
2.2. UML
2.3. 实现逻辑
3. 案例分析⭐️🔴
3.1. 电梯运行
通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。
3.1.1. 传统方案

- 会需要大量的 switch…case 这样的判断(ifelse 也是一样),使程序的可阅读性变差。
- 扩展性很差。
3.1.2. 状态模式

3.1.3. 实现逻辑⭐️🔴
Context 与 ConcreteState 相互聚合 +setter 导入
- 环境类 Context 聚合四种具体状态类
- 环境类 Context聚合抽象状态类 LiftState,并setter 导入,通过 LiftState 抽象状态类最终调用具体状态类的 4 种方法
- 抽象状态类 LiftState 又聚合了环境类 Context,并setter 导入,方便子类使用
- Client 首先要通过 Context 的 setLiftState传入一个具体状态类,来设置当前状态
- Client 中通过 Context 调用步骤 4 中传入的具体状态类实现的方法
- 在具体状态类中通过 (继承的抽象状态类LiftState中所聚合的)context 来改变 Context 中聚合的 liftState 的状态,然后通过调用 liftState 的方法,通过多态会调用刚刚设置 ConcreteState 的方法,从而达到状态流转
❕
3.1.4. 示例代码
[[pages/002-schdule/001-Arch/001-Subject/013-DemoCode/design_patterns/src/main/java/com/itheima/pattern/state/after/LiftState.java]]
3.2. APP 抽奖问题⭐️🔴
❕ ^b4joft
- 1)应用实例要求完成 APP 抽奖活动项目,使用状态模式
- 2)思路分析和图解(类图)
- 定义出一个接口叫状态接口,每个状态都实现它
- 接口有扣除积分方法、抽奖方法、发放奖品方法
3.2.1. UML
3.2.2. 实现逻辑
3.2.2.1. 角色分析
环境角色:RaffleActivity
抽象状态:State
具体状态:4 种抽奖状态,DispenseOutState、CanRaffleState、DispenseOutState、NoRaffleState
3.2.2.2. 关系逻辑
- 环境角色 RaffleActivity 聚合了抽象状态 State,根据业务逻辑调用 State 不同实现类的方法
- 具体状态类【聚合 + 构造导入】环境类 RaffleActivity,并调用方法
环境类 RaffleActivity 也可以放到抽象状态类中聚合,跟案例 1 一样,会更加优雅 - 具体状态类通过改变环境角色类 RaffleActivity 的状态,以及调用其方法来达到状态流转的目的
3.2.3. 示例代码
[[RaffleActivity.java]]
3.3. 借贷平台
借贷平台的订单,有审核 - 发布 - 抢单等等步骤,随着操作的不同,会改变订单的状态,项目中的这个模块实现就会使用到状态模式
3.3.1. 传统方案
通常通过 if/else
判断订单的状态,从而实现不同的逻辑,伪代码如下
1 |
|
3.3.1.1. 存在问题⭐️🔴
这类代码难以应对变化,在添加一种状态时,我们需要手动添加 if/else
,在添加一种功能时,要对所有的状态进行判断。因此代码会变得越来越臃肿,并且一旦没有处理某个状态,便会发生极其严重的 BUG,难以维护
3.3.2. 状态模式
3.3.3. 实现逻辑
与前面 2 个案例的不同之处在于具体状态类和抽象状态类都没有聚合 Context,而是通过调用方法时传入的,其他逻辑差不多
4. 适用场景⭐️🔴
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
❕
5. 优缺点⭐️🔴
5.1. 优点
- 高可读性:将每个状态的行为封装到对应的一个类中,条理清晰
- 易维护:将状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
5.2. 缺点
- 状态模式的使用必然会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。Context 和抽象状态类相互聚合 +setter 导入
- 状态模式对 “ 开闭原则 “ 的支持并不太好。因为具体状态类修改或增加后,context 也要修改或增加对应状态对象,比如状态常量,对状态对象方法的调用,以及状态流转中涉及到的其他状态类也要修改
6. JDK 源码分析
7. 实战经验
8. 参考与感谢
设计模式-2、设计模式及设计原则本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Taylor!
评论