• 设计模式之状态模式


    实际开发中订单往往都包含着订单状态,用户每进行一次操作都要切换对应的状态,而每次切换判断当前的状态是必须的,就不可避免的引入一系列判断语句,为了让代码更加清晰直观,我们引入今天的主角——状态模式。

    一、概念理解

    假设订单状态有,下单、发货、确认收货,如果用户确认收货,在常规编程中就要判断当前用户的状态,然后再修改状态,如果这种情况下使用状态模式。

    将各个状态都抽象成一个状态类,比如下单状态类、发货状态类、确认收货类,在状态类中处理相应的逻辑和控制下一个状态,在定义一个环境类,定义初始状态,并控制切换状态。

    在状态模式中应该包含着三个角色:

    环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,这个类持有State接口,负责保持并切换当前的状态。

    抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。

    具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

    以下为状态模式的类图,看起来是很直观的,理解起来也简单,我们需要说明的是状态模式的类图和策略模式的类图长的一样,但写起来状态模式比策略模式要难。

    image-20220926090555866

    我们要注意这段话,在状态模式中,类的行为是基于它的状态改变的,状态之间的切换,在状态A执行完毕后自己控制状态指向状态B,状态模式是不停的切换状态执行。这也是状态模式和策略模式不一样的地方。

    另外在状态模式中,状态A到B是由自己控制的,而不是由客户端来控制,这是状态模式和策略模式最显著的特征。

    我们基于订单状态案例实现demo。

    二、案例实现

    抽象状态:

    定义统一的状态切换方法

    /**
     * 抽象状态
     * @author tcy
     * @Date 20-09-2022
     */
    public abstract class OrderStateAbstract {
        protected Context context;
    
        public void setContext(Context context) {
            this.context = context;
        }
            /**
             * 状态切换
             */
        public abstract void handle();
    
    }
    

    具体状态-订单付款:

    实现状态接口,处理相应的逻辑,并定义下一个状态

    /**
     * 订单付款
     * @author tcy
     * @Date 21-09-2022
     */
    public class OrderStatePay extends OrderStateAbstract {
       @Override
        public void handle() {
            System.out.println("订单已支付,执行下个状态...");
            context.changeState(new OrderStateOut());
    
        }
    }
    

    具体状态-订单发货

    /**
     * 订单发货
     * @author tcy
     * @Date 21-09-2022
     */
    public class OrderStateOut extends OrderStateAbstract {
         @Override
        public void handle() {
            System.out.println("订单已经发货,开始下一状态...");
            context.changeState(new OrderStateSubmit());
        }
    }
    

    具体状态-订单确认收货

    /**
     * 订单提交
     * @author tcy
     * @Date 21-09-2022
     */
    public class OrderStateSubmit extends OrderStateAbstract {
        @Override
        public void handle() {
            System.out.println("订单已经确认收货...");
        }
    }
    

    环境类:

    持有最新状态,并调用具体的状态切换方法

    /**
     * 环境类
     * @author tcy
     * @Date 20-09-2022
     */
    public class Context {
    
       private OrderStateAbstract state;
    
        //定义环境类的初始状态
        public Context() {
            this.state = new OrderStatePay();
            state.setContext(this);
        }
    
    	//状态切换
        public void changeState(OrderStateAbstract state) {
            this.state = state;
            this.state.setContext(this);
        }
    
    
        /**
         * 审批通过请求
         */
        public void request() {
            this.state.handle();
        }
    }
    

    客户端调用:

    /**
     * @author tcy
     * @Date 20-09-2022
     */
    public class Client {
        public static void main(String[] args) {
    
             //创建环境
            Context context = new Context();
            //订单付款
            context.request();
            //订单发货
            context.request();
            //订单付款
            context.request();
    
        }
    }
    

    状态模式客户端调用比较简单,由状态内部类进行状态切换。

    三、总结

    很多博客都将策略模式的案例代码当做状态模式来讲解,这是不正确的,读者可以参考策略模式两篇做对比学习,认真体会他们之间的区别。

    在实际开发中,当控制一个对象状态转换的条件表达式过于复杂时,就可以使用状态模式把相关“判断逻辑”提取出来,用各个不同的类进行表示。

    系统处于哪种情况,直接使用相应的状态类对象进行处理,这样能把原来复杂的逻辑判断简单化,消除了 if-else、switch-case 等冗余语句,代码更有层次性,并且具备良好的扩展力。

    比如审批流程,我们案例也仅仅是用于订单流程做例子,在实际开发中并不会使用这种方式处理订单,因为订单的处理逻辑实际上并不是那么复杂,引入状态模式反而增加了更多的类,造成系统更加的复杂,这也是设计模式最显著的缺点。

    设计模式的学习要成体系,推荐你看我往期发布的设计模式文章。

    一、设计模式概述

    二、设计模式之工厂方法和抽象工厂

    三、设计模式之单例和原型

    四、设计模式之建造者模式

    五、设计模式之代理模式

    六、设计模式之适配器模式

    七、设计模式之桥接模式

    八、设计模式之组合模式

    九、设计模式之装饰器模式

    十、设计模式之外观模式

    十一、外观模式之享元模式

    十二、设计模式之责任链模式

    十三、设计模式之命令模式

    十四、设计模式之解释器模式

    十五、设计模式之迭代器模式

    十六、设计模式之中介者模式

    十七、设计模式之备忘录模式

    十八、设计模式之观察者模式

  • 相关阅读:
    基于python的电影爬虫可视化系统设计与实现
    STM32实现多级菜单界面显示
    Java学生管理系统
    WPF入门教程系列二十八 ——DataGrid使用示例MVVM模式(6)
    【算法】选择排序
    如何通过更好的文档管理减轻小型企业的压力
    (免费领源码)Java#SpringBoot#mysql高校实验室资产管理系统85189-计算机毕业设计项目选题推荐
    [数据结构-线性表1.2] 链表与 LinkedList<T>(.NET 源码学习)
    Spring Security OAuth Client配置加载源码分析
    5.什么是Spring的依赖注入(DI)?IOC和DI的区别是什么
  • 原文地址:https://www.cnblogs.com/tianClassmate/p/16733554.html