23种设计模式(七)-状态设计模式

2021-07-13 11:24:46 浏览数 (1)

一. 什么是状态模式?

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

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

代码语言:javascript复制
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. 环境类

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

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

代码语言:javascript复制
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抽象状态类

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

代码语言:javascript复制
/**
 * 状态抽象类
 */
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抽象类, 并实现抽象方法。

代码语言:javascript复制
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,执行行为观察结果。

代码语言:javascript复制
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();
    }
}

全部源码:

代码语言:javascript复制
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来记录当前的状态。并定义了所有状态的方法。

代码语言:javascript复制
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();
    }
}

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

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

代码语言:javascript复制
package com.lxl.www.designPatterns.statePattern.order;

/**
 * 订单状态
 */
public interface IOrderStatus {
    void businessHandler(OrderContext context);

    void printInfo();
}

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

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

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

代码语言:javascript复制
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("当前状态::订单完成");
    }
}

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

代码语言:javascript复制
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();
    }
}

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

开始下单 当前状态等::待付款 付款 付款完成 当前状态等::待出库 货物出库 货物已出库 当前状态等::待收货 收货 已经收货 当前状态等::待评价 评价订单 已经评价 当前状态::订单完成 订单完成 完成工作处理完毕

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

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

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

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

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

代码语言:javascript复制
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);
    }

}

第二步:定义抽象状态类

代码语言:javascript复制
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个状态。

待付款

代码语言:javascript复制
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) {

    }
}

待出库

代码语言:javascript复制
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) {

    }
}

待收货

代码语言:javascript复制
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) {

    }
}

确认收货

代码语言:javascript复制
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) {

    }
}

订单评价

代码语言:javascript复制
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) {

    }
}

订单完成

代码语言:javascript复制
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("订单完成");
    }
}

取消付款

代码语言:javascript复制
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) {

    }
}

退货退款

代码语言:javascript复制
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) {

    }
}

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

第四步: 客户端调用

代码语言:javascript复制
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();
    }
}

来看看运行效果:

张三 开始下单 开始购物 当前状态::待付款 =取消付款==== 取消付款 订单完成 李四 开始下单 开始购物 当前状态::待付款 =付款 付款完成了, 待出库 当前状态::待出库 =退货退款 申请退货 当前状态 :: 退货退款 退款完成 订单完成 王五 开始下单 开始购物 当前状态::待付款 =付款 付款完成了, 待出库 当前状态::待出库 =出库 出库完成, 待收货 当前状态 :: 待收货 =收货 进行收货, 完成收货动作 当前状态 :: 确认收货 =确认收货 确认收货了 当前状态 :: 订单待评价 =订单评价 订单评价完了 =订单完成 订单完成

第三种:完善方案二

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

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

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

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

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

代码语言:javascript复制
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);
    }
}

第二步: 定义抽象状态类

代码语言:javascript复制
package com.lxl.www.designPatterns.statePattern.order3;

/**
 * 订单状态
 */
public interface IOrderStatus {

}

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

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

待付款

代码语言:javascript复制
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());
    }

}

待出库

代码语言:javascript复制
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());
    }
}

待收货

代码语言:javascript复制
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());

    }
}

确认收货

代码语言:javascript复制
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());
    }
}

订单评价

代码语言:javascript复制
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());
    }
}

订单完成

代码语言:javascript复制
package com.lxl.www.designPatterns.statePattern.order3;

/**
 * 订单完成
 */
public class Finish implements IOrderStatus {

    public void finish(OrderContext context) {
        System.out.println("订单完成");
    }
}

取消付款

代码语言:javascript复制
package com.lxl.www.designPatterns.statePattern.order3;

/**
 * 取消付款
 */
public class CancelPay implements IOrderStatus {


    /*
     * 取消付款
     */
    public void cancelPay(OrderContext context) {
        System.out.println("执行取消付款逻辑");
        context.setOrderStatus(new Finish());
    }

}

退货退款

代码语言:javascript复制
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());
    }
}

第四步:客户端调用

代码语言:javascript复制
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个。因为状态越多,逻辑越负责,后期维护成本越高。

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

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

0 人点赞