前言
状态模式是一种行为模式,用于解决系统中复杂的对象状态转换以及各个状态下的封装等问题。状态模式是将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象的状态可以灵活多变。这样在客户端使用时无需关心对象的状态,可以实现自身的一致性处理。最近工作有些忙,更新博客慢了。还是要严格要求自己的,抽时间也要坚持学习。
状态模式
概念介绍
状态模式允许一个对象在其状态改变时,改变它的行为,对象看起来似乎修改了它的类。
想要在改变自身状态时改变对象行为,最直接的方法就是在代码中将所有可能发生的情况都考虑到了,然后使用if-else语句来进行相应的选择。但是这种方法对于复杂的状态判断会显得杂乱无章,容易产生错误;而且增加一个新的状态将会带来大量的修改。
流程结构如下图:
此时“能够修改自身”的状态模式的引入也许是个不错的主意,将不同条件下的行为封装在一个类里,再给这些类一个统一的父类来约束它们。
就会变成如下图流程结构:
举例
还是来举个具体的实例来说明一下吧。同事小王要请假,根据公司规定,请假要先在OA上提请假申请单,然后审批通过后就可以正常休假了。这个请假申请单大致经历这么几个状态,未审核(待提交)、审核中、审核通过、审核未通过,这里只是粗略的分为这几个状态。下面我们使用状态模式来模拟实现一下这个请假申请单的状态流转过程。
先创建一个休假申请单类
/** * 休假申请 */ public class LeaveApply { /** * 休假申请单初始状态是待提交状态 */ private ApplyState applyState = new UnAudited(); /** * 设置状态 * @param state */ public void setState(ApplyState state){ applyState = state; } /** * 状态变化后,更新对象自身的行为 */ public void update(){ applyState.changeHandle(); } }
审批单状态接口,声明统一处理的方法。
/** * 审批单状态接口 */ public interface ApplyState { /** * 状态变化处理操作 */ void changeHandle(); }
待提交状态
/** * 未审核状态(待提交审核) */ public class UnAudited implements ApplyState { /** * 状态变化处理操作 */ @Override public void changeHandle() { System.out.println("申请单处于未审核状态,当用户查看申请单详情时直接跳转到编辑页。"); } }
审核中状态
/** * 审核中状态 */ public class Audit implements ApplyState { /** * 状态变化处理操作 */ @Override public void changeHandle() { System.out.println("申请单处于审核中状态,当用户查看申请单详情时跳转到详情页可以看到提交记录。"); } }
审核通过状态
/** * * 审核通过状态 */ public class Pass implements ApplyState { /** * 状态变化处理操作 */ @Override public void changeHandle() { System.out.println("申请单已经审批通过,当前用户可以正常休假了。"); } }
审核未通过状态
/** * 审核未通过状态 */ public class NotPass implements ApplyState { /** * 状态变化处理操作 */ @Override public void changeHandle() { System.out.println("申请单未通过审核,当前用户不可以休假"); } }
测试类
public class TestOA { public static void main(String[] args) { //创建一个请假申请单 LeaveApply leaveApply = new LeaveApply(); leaveApply.setState(new UnAudited()); leaveApply.update(); leaveApply.setState(new Audit()); leaveApply.update(); leaveApply.setState(new Pass()); leaveApply.update(); leaveApply.setState(new NotPass()); leaveApply.update(); } }
运行结果:
申请单处于未审核状态,当用户查看申请单详情时直接跳转到编辑页。
申请单处于审核中状态,当用户查看申请单详情时跳转到详情页可以看到提交记录。
申请单已经审批通过,当前用户可以正常休假了。
申请单未通过审核,当前用户不可以休假
这个例子如果是不使用状态模式的思想,而是使用条件语句来实现就会出现很多的if-else来进行判断什么状态,应该执行什么样的方法。现在把各个状态的处理逻辑分离,结构清晰了并且耦合也不那么紧密了。
结构分析
在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心。状态模式的结构组成如下图:
在状态模式中,主要涉及了如下几个角色。
环境角色(Context):定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
抽象状态角色(State):定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。
具体状态角色(Contract):每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
总结
状态模式在功能上和策略模式很类似,但是在实现思想上状态模式是将各个状态分离解耦的,并且可以将对象的具体行为委托给当前的状态对象,而策略模式中,策略的选择是根据Context类中的行为来确定的,也不存在各个状态的切换。在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。
主要优点
1、封装了状态的转换规则,在状态模式中可以将状态转换的工作封装在环境类或具体的状态类中,可以对状态转换码进行集中管理,而不是分散在一个个的业务中。
2、将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。
3、允许状态转换逻辑与状态对象合为一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。
主要缺点
1、状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。
使用场景
1、对象的行为依赖于它的状态(如某些属性值),状态的改变将导致行为的变化。
2、在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。
想了解更多的设计模式请查看Java设计模式学习记录-GoF设计模式概述。
这个是我的个人公众号,文章以后也会同步到公众号上去,欢迎关注。