秒懂设计模式之状态模式(State Pattern)

Posted ShuSheng007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了秒懂设计模式之状态模式(State Pattern)相关的知识,希望对你有一定的参考价值。

[版权申明] 非商业目的注明出处可自由转载
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/116375477
出自:shusheng007

设计模式汇总篇,一定要收藏:

永不磨灭的设计模式(有这一篇真够了,拒绝标题党)

概述

状态模式在日常开发中是一个非常实用的模式,可以将你的代码逼格迅速提升一个档次,所以让我们开始今天的卓越之旅吧。

类型

行为型(behavioral)

难度

3颗星

定义

当一个对象内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

定义对于初学者没啥用,因为字都认识却无法理解其中的含义。必须等学完了,回过头来看才能更加深刻的理解其含义

使用场景

你发现你的代码里面存在一个很长的if else列表,而这些分支都是因为不同状态下执行的操作不一样时考虑使用此模式

UML类图

照例先上一张俺手撕的UML类图

在这里插入图片描述
从上图可见,状态模式共有3个角色

  • State

是一个接口,封装了状态及其行为

  • ConcreteState X

State的实现类,表示具体的状态

  • Context

保持并切换各个状态,其持有一个State的引用。它将依赖状态的各种操作委托给不同的状态对象执行。其负责与客户端交互

实例

最近王二狗又要过生日了,近两年他内心中是非常抗拒过生日的,因为每过一个生日就意味着自己又老一岁,离被辞退的35岁魔咒又近了一步。可惜时间是不以人的意志为转移的,任何人都阻止不了时间的流逝,所以该过还的过。令二狗比较欣慰的时,这次过生日老婆送了他一个自己一直想要的机械键盘作为生日礼物… 翠花于是在二狗生日前3天在京东上下了一个单…

自从下单以来,二狗天天看物流状态信息,心心念念着自己的机械键盘快点到…

这个物流系统就很适合使用状态模式来开发,因为此过程存在很多不同的状态,例如接单,出库,运输,送货,收货,评价等等。而订单在每个不同的状态下的操作可能都不一样,例如在接单状态下,商家就需要通知仓库拣货,通知用户等等操作,其他状态类似

下面是实例的UML类图
在这里插入图片描述

第一,定义一个状态接口

此接口定义各个状态的统一操作接口

public interface LogisticsState {
    void doAction(JdLogistics context);
}

第二,定义一个物流Context类

此类持有一个LogisticsState 的引用,负责在流程中保持并切换状态

public class JdLogistics {
    private LogisticsState logisticsState;

    public void setLogisticsState(LogisticsState logisticsState) {
        this.logisticsState = logisticsState;
    }

    public LogisticsState getLogisticsState() {
        return logisticsState;
    }
    
    public void doAction(){
        Objects.requireNonNull(logisticsState);
        logisticsState.doAction(this);
    }
}

第三,实现各种状态类

  • 接单状态类,其需要实现LogisticsState接口
public class OrderState implements LogisticsState {
    @Override
    public void doAction(JdLogistics context) {
        System.out.println("商家已经接单,正在处理中...");
    }
}
  • 出库状态类
public class ProductOutState implements LogisticsState {
    @Override
    public void doAction(JdLogistics context) {
        System.out.println("商品已经出库...");
    }
}

依次类推,可以建立任意多个状态类

第四, 客户端使用

public class StateClient {

    public void buyKeyboard() {
        //状态的保持与切换者
        JdLogistics jdLogistics = new JdLogistics();

        //接单状态
        OrderState orderState = new OrderState();
        jdLogistics.setLogisticsState(orderState);
        jdLogistics.doAction();

        //出库状态
        ProductOutState productOutState = new ProductOutState();
        jdLogistics.setLogisticsState(productOutState);
        jdLogistics.doAction();

        //运输状态
        TransportState transportState = new TransportState();
        jdLogistics.setLogisticsState(transportState);
        jdLogistics.doAction();
    }
}

输出结果:

商家已经接单,正在处理中...
商品已经出库...
商品正在运往天津分发中心

可见,我们将每个状态下要做的具体动作封装到了每个状态类中,我们只需要切换不同的状态即可。如果不使用状态模式,我们的代码中可能会出现很长的if else列表,这样就不便于扩展和修改了。

技术要点总结

  • 必须要有一个Context类,这个类持有State接口,负责保持并切换当前的状态。
  • 状态模式没有定义在哪里进行状态转换,本例是在Context类进行的,也有人在具体的State类中转换

当使用Context类切换状态时,状态类之间互相不认识,他们直接的依赖关系应该由客户端负责。
例如,只有在接单状态的操作完成后才应该切换到出库状态,那么出库状态就对接单状态有了依赖,这个依赖顺序应该由客户端负责,而不是在状态内判断。

当使用具体的State类切换时,状态直接就可能互相认识,一个状态执行完就自动切换到了另一个状态去了

优缺点

优点

  • 增强了程序的可扩展性,因为我们很容易添加一个State
  • 增强了程序的封装性,每个状态的操作都被封装到了一个状态类中

缺点

类变多了

状态模式与策略模式

状态模式与策略模式的UML类图都是一样的,从表面上看他们非常相似。特别是将状态切换任务放在Context中做的时候就更像了,但是其背后的思想却非常不同。

  • 策略模式定义了一组可互相代替的算法,这一组算法对象完成的是同一个任务,只是使用的方式不同,例如同样是亿万富翁,马云通过卖东西实现,而王思聪通过继承实现。
  • 状态模式不同的状态完成的任务完全不一样。

总结

设计模式值得你可以练习!

最后,如果你从本文中有所收获,可否点赞转发支持一下博主,你小小的鼓励,是激发博主持续写作的动力…

GitHub源码地址design-patterns

以上是关于秒懂设计模式之状态模式(State Pattern)的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之策略模式和状态模式(strategy pattern & state pattern)

秒懂设计模式之原型模式(Prototype Pattern)

秒懂设计模式之原型模式(Prototype Pattern)

秒懂设计模式之外观模式(Facade Pattern)

秒懂设计模式之组合模式(Composite Pattern)

秒懂设计模式之迭代器模式(Iterator Pattern)