zoukankan      html  css  js  c++  java
  • 23种设计模式(七)-状态设计模式

    一. 什么是状态模式?

    状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换。当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。例如:淘宝下单,订单有待付款,已付款待发货,待收货,待评价, 已完成等状态。每个状态对应的行为都是不同的, 一个状态完成会流转到下一个状态。

    通常对有状态的对象进行编程,我们的解决方案是:思考可能存在的所有状态,然后使用 if-else 或 switch-case 语句来进行状态判断,然后再根据不同的状态进行不同的处理。如上面的案例--淘宝下单:

    public class ClientApplication {
        public static void main(String[] args) {
            String status = "待付款";
            if ("待付款".equals(status)) {
                // 执行付款逻辑
            } else if("待发货".equals(status)) {
                // 执行发货逻辑
            } else if("待收货".equals(status)) {
                // 执行收货逻辑
            } else if("待评价".equals(status)) {
                // 执行评价逻辑
            } else if("待收货".equals(status)) {
                // 执行收获逻辑
            }
        }
    }
    

    大量的if...else的缺点很明显

    1. 违背开闭原则: 当增加一种状态的时候, 需要修改原来的逻辑
    2. 当状态很多的时候, 代码段很长, 臃肿, 不容易维护, 可扩展性差.

    状态模式可以很好地解决这个问题。
    状态模式的思想:当是条件语句表示一个对象状态转换过于复杂时,可以把条件判断中的“判断逻辑”提取出来,放在单独的类中,当前上下文处于那种状态,直接用相应的状态类对象进行处理,这样的好处是:能把原来复杂的判断逻辑简单化,消除了 if-else、switch-case 等条件判断语句,代码更有层次性,且具备良好的扩展力, 可维护性。

    二. 状态模式的结构

    状态模式把因环境改变而变化的对象行为包装在不同的状态类中,其目的保证对象的状态变化时, 其行为也随之改变。接下来, 我们来看看状态模式的结构。

    从中我们可以看出有四个组成部分:

    1. 环境类

    环境类也是上下文类, 状态的变化依赖于环境, 环境记录当前的状态. 环境类拥有多种状态的对象. 环境类的状态存在多样性, 不同状态下对象的行为有所不同, 因此将状态独立出去形成单独的状态类。

    > 1) 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
    > 2) 在环境类中定义所有状态执行的方法.
    

    package com.lxl.www.designPatterns.statePattern.context;
    
    /**
     * 上下文环境类
     */
    public class Context {
        // 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
        private State state;
        public Context() {
            this.state = new ConcreteStateA(this);
        }
    
        public void setState(State state) {
            this.state = state;
        }
    
        /**
         * 在环境类中定义所有状态执行的方法.
         */
        public void handler1() {
    
            this.state.handler1();
        }
    
        public void handler2() {
            this.state.handler2();
        }
    
    }
    
    
    

    2. State抽象状态类

      抽象环境中声明一个环境角色,提供各个状态类自行访问,并且提供所有状态的抽象行为,由各个实现类实现。

    /**
     * 状态抽象类
     */
    public abstract class State {
    
        /**
         * 环境上下文
         */
        public Context context;
        public State(Context context) {
            this.context = context;
        }
    
        /**
         * 定义了所有状态的抽象方法
         */
        abstract void handler1();
        abstract void handler2();
    }
    
    

    3. 具体状态

      具体状态实现,这里以定义ConcreteStateA、ConcreteStateB、ConcreteStateC三个具体状态类。具体状态类继承自State抽象类, 并实现抽象方法。

    package com.lxl.www.designPatterns.statePattern.context;
    
    public class ConcreteStateA extends State{
    
        public ConcreteStateA(Context context) {
            super(context);
        }
    
        @Override
        void handler1() {
            System.out.println("执行ConcreteStateA中handler1的逻辑");
            this.context.setState(new ConcreteStateB(context));
        }
    
        @Override
        void handler2() {
            System.out.println("执行ConcreteStateA中handler2的逻辑");
            this.context.setState(new ConcreteStateC(context));
        }
    }
    

    在这里, 我们重写了状态类的状态方法。当执行完方法一的业务逻辑后, 将状态变更为 ConcreteStateB. 执行完handler2的业务逻辑后, 将状态变更为ConcreteStateC。
    这种自动变更状态, 在调用方是无感知的。

    4. Client客户端

      构建Context环境上下文类的实例对象,初始化时设置初始状态是ConcreteStateA,执行行为观察结果。

    package com.lxl.www.designPatterns.statePattern.context;
    
    public class Client {
        public static void main(String[] args) {
            Context context = new Context();
            context.handler1();
            context.handler1();
            context.handler1();
            context.handler2();
        }
    }
    
    

    全部源码:

    package com.lxl.www.designPatterns.statePattern.context;
    
    /**
     * 上下文环境类
     */
    public class Context {
        // 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
        private State state;
        public Context() {
            this.state = new ConcreteStateA(this);
        }
    
        public void setState(State state) {
            this.state = state;
        }
    
        /**
         * 在环境类中定义所有状态执行的方法.
         */
        public void handler1() {
    
            this.state.handler1();
        }
    
        public void handler2() {
            this.state.handler2();
        }
    
    }
    
    
    
    /**
     * 状态抽象类
     */
    public abstract class State {
    
        /**
         * 环境上下文
         */
        public Context context;
        public State(Context context) {
            this.context = context;
        }
    
        /**
         * 定义了所有状态的抽象方法
         */
        abstract void handler1();
        abstract void handler2();
    }
    
    
    
    public class ConcreteStateA extends State{
    
        public ConcreteStateA(Context context) {
            super(context);
        }
    
        @Override
        void handler1() {
            System.out.println("执行ConcreteStateA中handler1的逻辑");
            this.context.setState(new ConcreteStateB(context));
        }
    
        @Override
        void handler2() {
            System.out.println("执行ConcreteStateA中handler2的逻辑");
            this.context.setState(new ConcreteStateC(context));
        }
    }
    
    public class ConcreteStateB extends State{
        public ConcreteStateB(Context context) {
            super(context);
        }
    
        @Override
        void handler1() {
            System.out.println("执行ConcreteStateB中handler1的逻辑");
            this.context.setState(new ConcreteStateC(context));
        }
    
        @Override
        void handler2() {
            System.out.println("执行ConcreteStateB中handler2的逻辑");
            this.context.setState(new ConcreteStateA(context));
        }
    }
    
    
    
    public class ConcreteStateC extends State{
        public ConcreteStateC(Context context) {
            super(context);
        }
    
        @Override
        void handler1() {
            System.out.println("执行ConcreteStateC中handler1的逻辑");
            this.context.setState(new ConcreteStateA(context));
        }
    
        @Override
        void handler2() {
            System.out.println("执行ConcreteStateC中handler2的逻辑");
            this.context.setState(new ConcreteStateB(context));
        }
    }
    
    
    
    public class Client {
        public static void main(String[] args) {
            Context context = new Context();
            context.handler1();
            context.handler1();
            context.handler1();
            context.handler1();
        }
    }
    
    

    客户端直接结果分析:

    1. Context实例花的时候, 状态是 ConcreteStateA
    2. 第一次执行context.handler1();将状态切换到了ConcreteStateB
    3. 第二次执行context.handler1();将状态切换到了ConcreteStateC
    4. 第三次执行context.handler1();将状态切换到了ConcreteStateA

    所以, 最后的运行结果是:

    执行ConcreteStateA中handler1的逻辑

    执行ConcreteStateB中handler1的逻辑

    执行ConcreteStateC中handler1的逻辑

    执行ConcreteStateA中handler1的逻辑

    三. 状态模式的应用实例

    下面以商品下单为例,来看看状态模式的应用。

    在电商系统购物的过程中,订单一般有这几种状态,待付款,待出库,待收货, 待评价,完成。不同的状态下,用户看到的行为是不同的。

    我们由浅入深, 来看看几种状态模式的实现。

    第一种: 状态是一条线的实现逻辑

    先来看看这种方法的流程:

    这种状态的特点是: 上一个状态流转到下一个状态, 下一个流转到下下个,以此类推。直到最后一个没有状态流转了。来看看源码:

    第一步:定义上下文环境类。

    用orderStatus来记录当前的状态。并定义了所有状态的方法。

    package com.lxl.www.designPatterns.statePattern.order;
    
    /**
     * 订单上下文
     */
    public class OrderContext {
    
        /**在上下文类中记录订单状态*/
        private IOrderStatus orderStatus;
    
        public OrderContext() {
            // 最开始是待付款状态
            this.orderStatus = new Pending();
        }
    
        public void setOrderStatus(IOrderStatus orderStatus) {
            this.orderStatus = orderStatus;
        }
    
        /**
         * 业务逻辑操作
         */
        public void businessHandler() {
            this.orderStatus.businessHandler(this);
        }
    
        /**
         * 打印当前业务
         */
        public void printInfo() {
            this.orderStatus.printInfo();
        }
    }
    
    

    第二步: 定义状态抽象类。

    里面定义所有状态类要实现的方法。

    package com.lxl.www.designPatterns.statePattern.order;
    
    /**
     * 订单状态
     */
    public interface IOrderStatus {
        void businessHandler(OrderContext context);
    
        void printInfo();
    }
    
    

    这里面就有两个方法, 一个是状态要执行的业务逻辑, 另一个是打印当前状态。

    第三步:定义状态具体类。

    这里定义了5个具体类,分别是:待付款,待出库,待收货, 待评价,完成

    package com.lxl.www.designPatterns.statePattern.order;
    /**
     * 待付款
     */
    public class Pending implements IOrderStatus{
        @Override
        public void businessHandler(OrderContext context) {
            //执行业务, 完成付款, 进入到下一个状态
            System.out.println("付款完成");
            context.setOrderStatus(new WaitOut());
        }
    
        @Override
        public void printInfo() {
            System.out.println("当前状态等::待付款");
        }
    }
    
    
    package com.lxl.www.designPatterns.statePattern.order;
    /**
     * 待出库
     */
    public class WaitOut implements IOrderStatus{
        @Override
        public void businessHandler(OrderContext context) {
            //执行业务, 完成付款, 进入到下一个状态
            System.out.println("货物已出库");
            context.setOrderStatus(new WaitReceive());
        }
    
        @Override
        public void printInfo() {
            System.out.println("当前状态等::待出库");
        }
    }
    
    
    package com.lxl.www.designPatterns.statePattern.order;
    /**
     * 待收货
     */
    public class WaitReceive implements IOrderStatus {
        @Override
        public void businessHandler(OrderContext context) {
            //执行业务, 完成付款, 进入到下一个状态
            System.out.println("已经收货");
            context.setOrderStatus(new OrderEvaluation());
        }
    
        @Override
        public void printInfo() {
            System.out.println("当前状态等::待收货");
        }
    }
    
    
    package com.lxl.www.designPatterns.statePattern.order;
    /**
     * 订单评价
     */
    public class OrderEvaluation implements IOrderStatus{
        @Override
        public void businessHandler(OrderContext context) {
            //执行业务, 完成付款, 进入到下一个状态
            System.out.println("已经评价");
            context.setOrderStatus(new Finish());
        }
    
        @Override
        public void printInfo() {
            System.out.println("当前状态等::待评价");
        }
    }
    
    
    package com.lxl.www.designPatterns.statePattern.order;
    /**
     * 订单完成
     */
    public class Finish implements IOrderStatus{
        @Override
        public void businessHandler(OrderContext context) {
            // 执行业务, 完成付款, 进入到下一个状态
            System.out.println("完成工作处理完毕");
        }
    
        @Override
        public void printInfo() {
            System.out.println("当前状态::订单完成");
        }
    }
    
    
    

    第四步: 定义客户端类, 模拟下单的流程

    package com.lxl.www.designPatterns.statePattern.order;
    
    public class OrderClient {
        public static void main(String[] args) {
            OrderContext orderContext = new OrderContext();
    
            // 开始下单
            System.out.println("==========开始下单==========");
            orderContext.printInfo();
    
            // 付款
            System.out.println("==========付款==========");
            orderContext.businessHandler();
            orderContext.printInfo();
    
            // 货物出库
            System.out.println("==========货物出库==========");
            orderContext.businessHandler();
            orderContext.printInfo();
    
            // 收货
            System.out.println("==========收货==========");
            orderContext.businessHandler();
            orderContext.printInfo();
    
            // 评价订单
            System.out.println("==========评价订单==========");
            orderContext.businessHandler();
            orderContext.printInfo();
    
            // 订单完成
            System.out.println("==========订单完成==========");
            orderContext.businessHandler();
        }
    }
    

    接下来我们来看看运行效果:

    开始下单

    当前状态等::待付款

    付款

    付款完成

    当前状态等::待出库

    货物出库

    货物已出库

    当前状态等::待收货

    收货

    已经收货

    当前状态等::待评价

    评价订单

    已经评价

    当前状态::订单完成

    订单完成

    完成工作处理完毕

    第二种: 带有取消付款和退货退款状态。

    退货退款最开始只能是收到货以后才能退。

    这种方式和第一个有所不同, 待付款和确认收货不止有一个状态流转,当有多个状态流转,甚至是更多的状态是,我们应该如何处理呢?

    第一步: 定义上下文环境类。

    这里环境上下文定义了所有的状态方法。

    package com.lxl.www.designPatterns.statePattern.order2;
    
    /**
     * 订单上下文
     */
    public class OrderContext {
    
        /**在上下文类中记录订单状态*/
        private IOrderStatus orderStatus;
    
        public OrderContext() {
            System.out.println("开始购物");
            // 最开始是待付款状态
            this.orderStatus = new Pending();
        }
    
        public void setOrderStatus(IOrderStatus orderStatus) {
            this.orderStatus = orderStatus;
        }
    
    
        /**
         * 付款完成
         */
        public void pending() {
            this.orderStatus.pending(this);
        }
    
        public void waitOut() {
            this.orderStatus.waitOut(this);
        }
    
        public void waitReceive() {
            this.orderStatus.waitReceive(this);
        }
    
        public void confirmReceived() {
            this.orderStatus.confirmReceived(this);
        }
    
        public void orderEvaluation() {
            this.orderStatus.orderEvaluation(this);
        }
    
        public void finish() {
            this.orderStatus.finish(this);
        }
    
        public void cancelPay() {
            this.orderStatus.cancelPay(this);
        }
    
        public void refunds() {
            this.orderStatus.refunds(this);
        }
    
    }
    
    

    第二步:定义抽象状态类

    package com.lxl.www.designPatterns.statePattern.order2;
    
    /**
     * 订单状态
     */
    public interface IOrderStatus {
        /*
         * 待付款
         */
        void pending(OrderContext context);
    
        /*
         * 取消付款
         */
        void cancelPay(OrderContext context);
    
        /*
         * 待出库
         */
        void waitOut(OrderContext context);
    
        /*
         * 退货退款
         */
        void refunds(OrderContext context);
    
        /*
         * 待收货
         */
        void waitReceive(OrderContext context);
    
        /*
         * 确认收货
         */
        void confirmReceived(OrderContext context);
    
        /*
         * 订单评价
         */
        void orderEvaluation(OrderContext context);
    
        /*
         * 订单完成
         */
        void finish(OrderContext context);
    
    }
    
    

    第三步:定义具体状态类。

    我们这里定义了待付款,待出库,待收货, 确认收货,订单评价,订单完成,取消付款,退货退款一共8个状态。

    待付款

    package com.lxl.www.designPatterns.statePattern.order2;
    
    /**
     * 待付款
     */
    public class Pending implements IOrderStatus {
        public Pending() {
            System.out.println("当前状态::待付款");
    
        }
    
        @Override
        public void pending(OrderContext context) {
            System.out.println("付款完成了, 待出库");
            context.setOrderStatus(new WaitOut());
        }
    
        /*
         * 取消付款
         */
        public void cancelPay(OrderContext context) {
            System.out.println("取消付款");
            context.setOrderStatus(new Finish());
        }
    
        @Override
        public void waitOut(OrderContext context) {
            System.out.println("去付款-->付款完成,待出库");
            context.setOrderStatus(new WaitOut());
        }
    
        /*
         * 退货退款
         */
        public void refunds(OrderContext context) {
    
        }
    
        @Override
        public void waitReceive(OrderContext context) {
    
        }
    
        @Override
        public void confirmReceived(OrderContext context) {
    
        }
    
        @Override
        public void orderEvaluation(OrderContext context) {
    
        }
    
        @Override
        public void finish(OrderContext context) {
    
        }
    }
    
    

    待出库

    package com.lxl.www.designPatterns.statePattern.order2;
    
    /**
     * 待出库
     */
    public class WaitOut implements IOrderStatus {
    
        public WaitOut() {
            System.out.println("当前状态::待出库");
        }
    
        @Override
        public void pending(OrderContext context) {
    
        }
    
        @Override
        public void cancelPay(OrderContext context) {
    
        }
    
        /*
         * 退货退款
         */
        public void refunds(OrderContext context) {
            System.out.println("申请退货");
            context.setOrderStatus(new Refunds());
        }
    
        @Override
        public void waitOut(OrderContext context) {
            System.out.println("出库完成, 待收货");
            context.setOrderStatus(new WaitReceive());
        }
    
        @Override
        public void waitReceive(OrderContext context) {
    
        }
    
        @Override
        public void confirmReceived(OrderContext context) {
    
        }
    
        @Override
        public void orderEvaluation(OrderContext context) {
    
        }
    
        @Override
        public void finish(OrderContext context) {
    
        }
    }
    
    

    待收货

    package com.lxl.www.designPatterns.statePattern.order2;
    
    /**
     * 待收货
     */
    public class WaitReceive implements IOrderStatus {
    
        public WaitReceive() {
            System.out.println("当前状态 :: 待收货");
        }
    
        @Override
        public void pending(OrderContext context) {
    
        }
    
        @Override
        public void cancelPay(OrderContext context) {
    
        }
    
        @Override
        public void waitOut(OrderContext context) {
    
        }
    
        /*
         * 退货退款
         */
        public void refunds(OrderContext context) {
            System.out.println("申请退货");
            context.setOrderStatus(new Refunds());
        }
    
        @Override
        public void waitReceive(OrderContext context) {
            System.out.println("进行收货, 完成收货动作");
            context.setOrderStatus(new Confirm());
    
        }
    
        @Override
        public void confirmReceived(OrderContext context) {
    
        }
    
        @Override
        public void orderEvaluation(OrderContext context) {
    
        }
    
        @Override
        public void finish(OrderContext context) {
    
        }
    }
    
    

    确认收货

    package com.lxl.www.designPatterns.statePattern.order2;
    
    public class Confirm implements IOrderStatus{
    
        public Confirm() {
            System.out.println("当前状态 :: 确认收货");
        }
        @Override
        public void pending(OrderContext context) {
    
        }
    
        @Override
        public void cancelPay(OrderContext context) {
    
        }
    
        @Override
        public void waitOut(OrderContext context) {
    
        }
    
        @Override
        public void refunds(OrderContext context) {
            System.out.println("申请退款");
            context.setOrderStatus(new Refunds());
        }
    
        @Override
        public void waitReceive(OrderContext context) {
    
        }
    
        @Override
        public void confirmReceived(OrderContext context) {
            System.out.println("确认收货了");
            context.setOrderStatus(new OrderEvaluation());
        }
    
        @Override
        public void orderEvaluation(OrderContext context) {
    
        }
    
        @Override
        public void finish(OrderContext context) {
    
        }
    }
    
    

    订单评价

    package com.lxl.www.designPatterns.statePattern.order2;
    
    import com.sun.tools.corba.se.idl.constExpr.Or;
    
    /**
     * 订单评价
     */
    public class OrderEvaluation implements IOrderStatus {
    
        public OrderEvaluation() {
            System.out.println("当前状态 :: 订单待评价");
        }
    
        @Override
        public void pending(OrderContext context) {
    
        }
    
        @Override
        public void cancelPay(OrderContext context) {
    
        }
    
        @Override
        public void waitOut(OrderContext context) {
    
        }
    
        @Override
        public void refunds(OrderContext context) {
    
        }
    
        @Override
        public void waitReceive(OrderContext context) {
    
        }
    
        @Override
        public void confirmReceived(OrderContext context) {
    
        }
    
        @Override
        public void orderEvaluation(OrderContext context) {
            System.out.println("订单评价完了");
            context.setOrderStatus(new Finish());
        }
    
        @Override
        public void finish(OrderContext context) {
    
        }
    }
    
    

    订单完成

    package com.lxl.www.designPatterns.statePattern.order2;
    
    /**
     * 订单完成
     */
    public class Finish implements IOrderStatus {
    
        @Override
        public void pending(OrderContext context) {
    
        }
    
        @Override
        public void cancelPay(OrderContext context) {
    
        }
    
        @Override
        public void waitOut(OrderContext context) {
    
        }
    
        /*
         * 退货退款
         */
        public void refunds(OrderContext context) {
    
        }
    
        @Override
        public void waitReceive(OrderContext context) {
    
        }
    
        @Override
        public void confirmReceived(OrderContext context) {
    
        }
    
        @Override
        public void orderEvaluation(OrderContext context) {
    
        }
    
        @Override
        public void finish(OrderContext context) {
            System.out.println("订单完成");
        }
    }
    
    

    取消付款

    package com.lxl.www.designPatterns.statePattern.order2;
    
    /**
     * 取消付款
     */
    public class CancelPay implements IOrderStatus {
    
        @Override
        public void pending(OrderContext context) {
    
        }
    
        /*
         * 取消付款
         */
        public void cancelPay(OrderContext context) {
    
        }
    
        @Override
        public void waitOut(OrderContext context) {
    
        }
    
        /*
         * 退货退款
         */
        public void refunds(OrderContext context) {
    
        }
    
        @Override
        public void waitReceive(OrderContext context) {
    
        }
    
        @Override
        public void confirmReceived(OrderContext context) {
    
        }
    
        @Override
        public void orderEvaluation(OrderContext context) {
    
        }
    
        @Override
        public void finish(OrderContext context) {
    
        }
    }
    
    

    退货退款

    package com.lxl.www.designPatterns.statePattern.order2;
    
    public class Refunds implements IOrderStatus{
    
        public Refunds() {
            System.out.println("当前状态 :: 退货退款");
        }
    
        @Override
        public void pending(OrderContext context) {
    
        }
    
        @Override
        public void cancelPay(OrderContext context) {
    
        }
    
        @Override
        public void waitOut(OrderContext context) {
    
        }
    
        @Override
        public void refunds(OrderContext context) {
            System.out.println("退款完成");
            context.setOrderStatus(new Finish());
        }
    
        @Override
        public void waitReceive(OrderContext context) {
    
        }
    
        @Override
        public void confirmReceived(OrderContext context) {
    
        }
    
        @Override
        public void orderEvaluation(OrderContext context) {
    
        }
    
        @Override
        public void finish(OrderContext context) {
    
        }
    }
    
    

    我们看得出来, 这些状态子类,继承了父类所有的方法,但是却没有实现所有的方法。而是只实现了和自己有关系的一部分方法。这也是我们后面需要优化的地方

    第四步: 客户端调用

    package com.lxl.www.designPatterns.statePattern.order2;
    
    public class OrderClient {
        public static void main(String[] args) {
            System.out.println("==========张三   开始下单==========");
            OrderContext o1 = new OrderContext();
    
            System.out.println("===========取消付款==============");
            o1.cancelPay();
            o1.finish();
    
    
            System.out.println();
            System.out.println();
            System.out.println("==========李四    开始下单==========");
            OrderContext o2 = new OrderContext();
    
            System.out.println("===========付款==============");
            o2.pending();
    
            System.out.println("===========退货退款==============");
            o2.refunds();
            o2.refunds();
            o2.finish();
    
    
            System.out.println();
            System.out.println();
            System.out.println("==========王五    开始下单==========");
            OrderContext o3 = new OrderContext();
    
            System.out.println("===========付款==============");
            o3.pending();
    
            System.out.println("===========出库==============");
            o3.waitOut();
    
            System.out.println("===========收货==============");
            o3.waitReceive();
    
            System.out.println("===========确认收货==============");
            o3.confirmReceived();
    
            System.out.println("===========订单评价==============");
            o3.orderEvaluation();
    
            System.out.println("===========订单完成==============");
            o3.finish();
        }
    }
    
    

    来看看运行效果:

    张三 开始下单

    开始购物

    当前状态::待付款

    =取消付款====

    取消付款

    订单完成

    李四 开始下单

    开始购物

    当前状态::待付款

    =付款

    付款完成了, 待出库

    当前状态::待出库

    =退货退款


    申请退货

    当前状态 :: 退货退款

    退款完成

    订单完成

    王五 开始下单

    开始购物

    当前状态::待付款

    =付款

    付款完成了, 待出库

    当前状态::待出库

    =出库


    出库完成, 待收货

    当前状态 :: 待收货

    =收货

    进行收货, 完成收货动作

    当前状态 :: 确认收货

    =确认收货


    确认收货了

    当前状态 :: 订单待评价

    =订单评价

    订单评价完了

    =订单完成


    订单完成

    第三种:完善方案二

    方案二违背了接口隔离原则。胖接口,导致很多类其实都不需要重写方法,但是不得不继承。实际工作中,我们可以将胖接口拆分,这样状态子类只需要实现自己需要的接口就可以了。

    今天这里还有一种特别的解决方案,也可以很巧妙的解决这个问题。

    第一步: 定义环境上下文类

    观察一下,这次的环境上下文类和方案二的有什么区别?

    对每一个状态进行的强转。这样做的优点后面就可以看出来了

    package com.lxl.www.designPatterns.statePattern.order3;
    
    /**
     * 订单上下文
     */
    public class OrderContext {
    
        /**在上下文类中记录订单状态*/
        private IOrderStatus orderStatus;
    
        public OrderContext() {
            System.out.println("开始购物");
            // 最开始是待付款状态
            this.orderStatus = new Pending();
        }
    
        public void setOrderStatus(IOrderStatus orderStatus) {
            this.orderStatus = orderStatus;
        }
    
    
        /**
         * 去付款
         */
        public void toPay() {
            ((Pending)this.orderStatus).toPay(this);
        }
    
        /**
         * 取消付款
         */
        public void toCancelPay() {
            ((Pending)this.orderStatus).toCancelPay(this);
        }
    
        /**
         * 取消付款
         */
        public void cancelPay() {
            ((CancelPay)this.orderStatus).cancelPay(this);
        }
    
        /**
         * 去发货
         */
        public void toSendProduct() {
            ((WaitOut)this.orderStatus).toSendProduct(this);
        }
    
        /**
         * 申请退款
         */
        public void toRefunds() {
            ((Confirm)this.orderStatus).toRefunds(this);
        }
    
        /**
         * 已退款
         */
        public void refunded() {
            ((Refunds)this.orderStatus).refunded(this);
        }
    
        public void toReceiveProduct() {
            ((WaitReceive)this.orderStatus).toReceiveProduct(this);
        }
    
        /**
         * 点击确认收货按钮
         */
        public void toConfirmReceived() {
            ((Confirm)this.orderStatus).toConfirmReceived(this);
        }
    
        /**
         * 去评价订单
         */
        public void toOrderEvaluation() {
            ((OrderEvaluation)this.orderStatus).toOrderEvaluation(this);
        }
    
        /**
         * 已完成
         */
        public void finish() {
            ((Finish)this.orderStatus).finish(this);
        }
    }
    
    

    第二步: 定义抽象状态类

    package com.lxl.www.designPatterns.statePattern.order3;
    
    /**
     * 订单状态
     */
    public interface IOrderStatus {
    
    }
    
    

    抽象状态类里面并没有方法

    第三步: 定义具体状态实现

    待付款

    package com.lxl.www.designPatterns.statePattern.order3;
    
    /**
     * 待付款
     */
    public class Pending implements IOrderStatus {
        public Pending() {
            System.out.println("当前状态::待付款");
    
        }
    
        public void toPay(OrderContext context) {
            System.out.println("付款完成了, 待出库");
            context.setOrderStatus(new WaitOut());
        }
    
        /*
         * 取消付款
         */
        public void toCancelPay(OrderContext context) {
            System.out.println("不想要了, 要取消付款");
            context.setOrderStatus(new CancelPay());
        }
    
    }
    
    

    待出库

    package com.lxl.www.designPatterns.statePattern.order3;
    
    /**
     * 待出库
     */
    public class WaitOut implements IOrderStatus {
    
        public WaitOut() {
            System.out.println("当前状态::待出库");
        }
    
    
        /*
         * 退货退款
         */
        public void toRefunds(OrderContext context) {
            System.out.println("不想要了, 申请退货退款");
            context.setOrderStatus(new Refunds());
        }
    
        /**
         * 去发货
         * @param context
         */
        public void toSendProduct(OrderContext context) {
            System.out.println("出库完成, 待收货");
            context.setOrderStatus(new WaitReceive());
        }
    }
    
    

    待收货

    package com.lxl.www.designPatterns.statePattern.order3;
    
    /**
     * 待收货
     */
    public class WaitReceive implements IOrderStatus {
    
        public WaitReceive() {
            System.out.println("当前状态 :: 待收货");
        }
    
        /*
         * 退货退款
         */
        public void refunds(OrderContext context) {
            System.out.println("申请退货");
            context.setOrderStatus(new Refunds());
        }
    
        /**
         * 去收获
         * @param context
         */
        public void toReceiveProduct(OrderContext context) {
            System.out.println("执行收货逻辑, 完成收货操作");
            context.setOrderStatus(new Confirm());
    
        }
    }
    
    

    确认收货

    package com.lxl.www.designPatterns.statePattern.order3;
    
    public class Confirm implements IOrderStatus {
    
        public Confirm() {
            System.out.println("当前状态 :: 确认收货");
        }
    
        public void toRefunds(OrderContext context) {
            System.out.println("不想要了, 想退款");
            context.setOrderStatus(new Refunds());
        }
    
    
        public void toConfirmReceived(OrderContext context) {
            System.out.println("执行确认收货逻辑");
            context.setOrderStatus(new OrderEvaluation());
        }
    }
    
    

    订单评价

    package com.lxl.www.designPatterns.statePattern.order3;
    
    /**
     * 订单评价
     */
    public class OrderEvaluation implements IOrderStatus {
    
        public OrderEvaluation() {
            System.out.println("当前状态 :: 订单待评价");
        }
    
        public void toOrderEvaluation(OrderContext context) {
            System.out.println("订单评价完了");
            context.setOrderStatus(new Finish());
        }
    }
    
    

    订单完成

    package com.lxl.www.designPatterns.statePattern.order3;
    
    /**
     * 订单完成
     */
    public class Finish implements IOrderStatus {
    
        public void finish(OrderContext context) {
            System.out.println("订单完成");
        }
    }
    
    

    取消付款

    package com.lxl.www.designPatterns.statePattern.order3;
    
    /**
     * 取消付款
     */
    public class CancelPay implements IOrderStatus {
    
    
        /*
         * 取消付款
         */
        public void cancelPay(OrderContext context) {
            System.out.println("执行取消付款逻辑");
            context.setOrderStatus(new Finish());
        }
    
    }
    
    

    退货退款

    package com.lxl.www.designPatterns.statePattern.order3;
    
    public class Refunds implements IOrderStatus {
    
        public Refunds() {
            System.out.println("当前状态 :: 退货退款");
        }
    
    
        public void refunded(OrderContext context) {
            System.out.println("完成退款");
            context.setOrderStatus(new Finish());
        }
    }
    
    

    第四步:客户端调用

    package com.lxl.www.designPatterns.statePattern.order3;
    
    public class OrderClient {
        public static void main(String[] args) {
            System.out.println("==========张三   开始下单==========");
            OrderContext o1 = new OrderContext();
    
            System.out.println("===========取消付款==============");
            o1.toCancelPay();
            o1.cancelPay();
    
    
            System.out.println("");
            System.out.println("");
            System.out.println("==========李四    开始下单  收货后退款==========");
            OrderContext o2 = new OrderContext();
    
            System.out.println("===========付款==============");
            o2.toPay();
    
            System.out.println("===========发货==============");
            o2.toSendProduct();
            System.out.println("===========收获==============");
            o2.toReceiveProduct();
            System.out.println("===========退货==============");
            o2.toRefunds();
            o2.refunded();
            System.out.println("===========完成==============");
            o2.finish();
    
    
            System.out.println();
            System.out.println();
            System.out.println("==========王五    开始下单走完全流程==========");
            OrderContext o3 = new OrderContext();
    
            System.out.println("===========付款==============");
            o3.toPay();
    
            System.out.println("===========出库==============");
            o3.toSendProduct();
    
            System.out.println("===========收货==============");
            o3.toReceiveProduct();
    
            System.out.println("===========确认收货==============");
            o3.toConfirmReceived();
    
            System.out.println("===========订单评价==============");
            o3.toOrderEvaluation();
    
            System.out.println("===========订单完成==============");
            o3.finish();
    
        }
    }
    
    

    运行结果:

    张三 开始下单

    开始购物

    当前状态::待付款

    =取消付款====

    不想要了, 要取消付款

    执行取消付款逻辑

    李四 开始下单 收货后退款

    开始购物

    当前状态::待付款

    =付款

    付款完成了, 待出库

    当前状态::待出库

    =发货


    出库完成, 待收货

    当前状态 :: 待收货

    =收获

    执行收货逻辑, 完成收货操作

    当前状态 :: 确认收货

    =退货


    不想要了, 想退款

    当前状态 :: 退货退款

    完成退款

    =完成====

    订单完成

    王五 开始下单走完全流程

    开始购物

    当前状态::待付款

    =付款

    付款完成了, 待出库

    当前状态::待出库

    =出库


    出库完成, 待收货

    当前状态 :: 待收货

    =收货

    执行收货逻辑, 完成收货操作

    当前状态 :: 确认收货

    =确认收货


    执行确认收货逻辑

    当前状态 :: 订单待评价

    =订单评价

    订单评价完了

    =订单完成


    订单完成

    运行结果和方案二是一样的

    四、状态设计模式的优缺点

    优点:

    • 结构清晰,避免了过多的switch…case或if…else语句的使用
    • 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
    • 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
    • 封装性非常好,状态变化放置到了类的内部来实现,外部调用不需要知道类内部如何实现状态和行为的变换

    缺点:

    • 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维

      护难度

    • 违背开闭原则:增加一个状态,除了要增加状态子类,还需要修改原来的环境类。

    五、应用场景

    当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式

    • 电梯,有运行状态、开门状态、闭门状态、停止状态等
    • 一日从早到晚自身的状态,比如工作状态、学习状态、睡觉状态等等
    • 运动员可以有正常状态、非正常状态和超长状态

    六、注意事项

    • 在行为受状态约束的情况下可以使用状态模式,使用时对象的状态最好不要超过5个。因为状态越多,逻辑越负责,后期维护成本越高。

    七、状态模式对六大原则的使用分析

    • 单一职责原则: 符合单一职责原则,一个状态类只做一件事
    • 里式替换原则:父类出现的地方都可以使用子类替换。--- 状态模式父类是抽象类或者接口。方案一重写了父类的构造方法
    • 依赖倒置原则:依赖于抽象,而不是依赖于具体。---符合
    • 接口隔离原则:依赖于最小接口---方案二不符合
    • 迪米特法则:最小知识原则-之和朋友交流,减少和朋友的沟通 --- 符合
    • 开闭原则:对扩展开放,对修改关闭----增加状态需要修改原来的环境类---不符合


  • 相关阅读:
    2016多校赛1 A 期望 B SG博弈,状压 D 倍增,二分
    POWOJ 1739: 魔术球问题 DAG最小路径覆盖转最大流
    Codeforces 743D 树形dp
    线性规划与网络流24题 索引
    WangEditor富文本编辑器的简单使用,并将文本数据发往后台
    SSRF
    关于Blind XXE
    blind xxe攻击
    linux awk命令详解
    kali
  • 原文地址:https://www.cnblogs.com/ITPower/p/14980134.html
Copyright © 2011-2022 走看看