• 22、设计模式之状态模式(State)


    一、什么是状态模式
    状态模式是一种行为型设计模式。它允许对象在内部状态发生改变时改变它的行为,简单地讲就是,一个拥有状态的context对象,在不同状态下,其行为会发生改变。

    二、角色组成

    上下文(Context):定义客户端需要的接口,并且负责具体状态的切换。
    抽象状态(Abstract State):抽象状态类是所有具体状态类的基类或接口。负责定义该状态下的行为,可以一个或多个。
    具体状态(Concrete State):具体状态类实现了抽象状态类定义的接口,并根据当前状态决定上下文对象的行为。

    三、 优缺点
    优点:

    客户端只需要与上下文对象进行交互,而不需要了解具体状态对象的切换和行为实现细节。
    状态模式遵循开闭原则,使系统更加可扩展。当需要增加新的状态时,只需添加新的具体状态类,而无需修改上下文对象或其他状态类。
    避免了使用大量的条件语句来控制对象在不同状态下的行为。它将状态相关的代码分散到各个具体状态类中,使代码更加清晰、可读性更高,易于维护和扩展。
    状态转换被封装在具体状态类中,可根据需求定义不同的状态切换规则,使得状态转换过程可控、灵活。
    缺点:

    状态模式增加了系统中类的数量,引入了更多的类,可能会增加代码的复杂性。
    对于简单、直接的状态机,使用状态模式可能会显得过于繁琐,增加了代码的冗余。在这种情况下,可以采用简化的条件语句来处理状态转换。
    四、应用场景
    4.1 生活场景
    自动售货机:自动售货机中的商品状态会随着库存量的变化而改变。当库存为0时,商品状态为"售罄";当库存充足时,商品状态为"可购买";
    购物车状态:在线购物网站中,购物车可以有不同的状态,比如空、有商品、结算中等。每个状态下,购物车的显示内容和可用操作不同。

    4.2 java场景

    Thread类:线程有new、Runnable、Blocked、Waiting、Time_Waiting和Terminated状态。这些状态代表了线程在不同的执行阶段或操作中的不同状态。Thread类内部使用了状态模式来管理和切换线程的状态。
    Connection接口:Connection接口表示与数据库的连接,它可能处于不同的状态,比如打开、关闭、空闲、繁忙等。Connection接口在不同的状态下提供了不同的操作方法,以便与数据库进行交互。

    五、代码实现
    下面以自动售卖机为例,解释一下状态模式。我们可以将售卖机分为三个部分:自动售卖机(Machine)类作为上下文(Context),状态(State)作为状态的抽象状态(Abstract State),售卖机的三个状态——未投币(NoCoinState)、已投币(HasCoinState)、出售中(SlodState)作为具体状态(Concrete State)

    5.0 UML类图
    在这里插入图片描述
    5.1 State——抽象状态

    /**
     *
     * 1.抽象状态(Abstract State):状态接口
     * 定义:定义该状态下的行为,可以一个或多个。
     */
    public interface State {
        //投币
        void insertCoin();
        //选择商品
        void selectProduct();
        //发放商品
        void dispense();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5.2 具体状态

    /**
     * 
     * 2.具体状态(Concrete State):未投币状态
     * 定义:实现了抽象状态类定义的接口,并根据当前状态决定上下文对象的行为。
     */
    public class NoCoinState implements State{
        private Machine machine;
     
        public NoCoinState(Machine machine) {
            this.machine = machine;
        }
     
        @Override
        public void insertCoin() {
            System.out.println("已投币");
            // 切换到已投币状态
            machine.setState(machine.getHasCoinState());
        }
     
        @Override
        public void selectProduct() {
            System.out.println("请先投币");
        }
     
        @Override
        public void dispense() {
            System.out.println("请先投币选择商品");
        }
    }
    /**
     * 
     * 2.具体状态(Concrete State):已投币状态
     * 定义:实现了抽象状态类定义的接口,并根据当前状态决定上下文对象的行为。
     */
    public class HasCoinState implements State{
     
        private Machine machine;
     
        public HasCoinState(Machine machine) {
            this.machine = machine;
        }
     
        @Override
        public void insertCoin() {
            System.out.println("已投币,请勿重复投币");
        }
     
        @Override
        public void selectProduct() {
            System.out.println("商品已选择");
            // 切换到出售状态
            machine.setState(machine.getSoldState());
        }
     
        @Override
        public void dispense() {
            System.out.println("请先选择商品");
        }
    }
    /**
     *
     * 2.具体状态(Concrete State):出售状态
     * 定义:实现了抽象状态类定义的接口,并根据当前状态决定上下文对象的行为。
     */
    public class SoldState implements State{
        private Machine machine;
     
        public SoldState(Machine machine) {
            this.machine = machine;
        }
     
        @Override
        public void insertCoin() {
            System.out.println("正在出售商品,请稍等");
        }
     
        @Override
        public void selectProduct() {
            System.out.println("正在出售商品,请稍等");
        }
     
        @Override
        public void dispense() {
            System.out.println("商品已发放");
            //切换到未投币状态
            machine.setState(machine.getNoCoinState());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    5.3 Machine——上下文

    /** 
     * 3.上下文(Context):自动售卖机
     * 定义:定义客户端需要的接口,并且负责具体状态的切换。
     */
    public class Machine {
        //未投币状态
        private State noCoinState;
        //已投币状态
        private State hasCoinState;
        //出售状态
        private State soldState;
        //当前状态
        private State currentState;
     
        public Machine() {
            noCoinState = new NoCoinState(this);
            hasCoinState = new HasCoinState(this);
            soldState = new SoldState(this);
            currentState = noCoinState; // 初始状态为未投币状态
        }
     
        public void setState(State state) {
            currentState = state;
        }
     
        public State getNoCoinState() {
            return noCoinState;
        }
     
        public State getHasCoinState() {
            return hasCoinState;
        }
     
        public State getSoldState() {
            return soldState;
        }
     
        public void insertCoin() {
            currentState.insertCoin();
        }
     
        public void selectProduct() {
            currentState.selectProduct();
        }
     
        public void dispense() {
            currentState.dispense();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    5.4 testState

    /**
     *
     * 状态模式测试类
     */
    @SpringBootTest
    public class TestState {
     
        @Test
        void testState(){
            //创建上下文对象(自动售卖机)
            Machine machine = new Machine();
     
            System.out.println("=======直接选择商品=======");
            machine.selectProduct();
     
            System.out.println("======投币--->选择商品--->发放商品=======");
            machine.insertCoin();
            machine.selectProduct();
            machine.dispense();
     
            System.out.println("=======投币--->发放商品=======");
            machine.insertCoin();
            machine.dispense();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述
    六、总结
    熟悉策略模式的小伙伴可能会发现,状态模式和策略模式的UML类图几乎一摸一样,但这两中模式的应用场景是不一样的。策略模式的多种算法行为选择一种就能满足,彼此之间是独立的;而状态模式中各个状态间存在相互关系,彼此之间在一定条件下存在自动切换状态的效果,并且用户不能指定状态,只能设置初始状态。

    以下情况出现,可以考虑状态模式:

    当一个对象的行为取决于其内部状态,并且在不同状态下具有不同的行为时
    当对象的行为在运行时需要根据其状态动态改变,并且需要避免大量的条件语句和分支判断时。
    当代码中存在大量的条件语句和分支逻辑,并且这些逻辑与对象的状态变化相关时。
    当对象的状态之间存在复杂的转换关系,并且需要维护状态转换的一致性时。

  • 相关阅读:
    Hive用户中文使用手册系列(二)
    无需人工先验!港大&同济&LunarAI&旷视提出基于语义分组的自监督视觉表征学习,显著提升目标检测、实例分割和语义分割任务!...
    SQL Server详细使用教程及常见问题解决
    Seata概述基础
    C++中的自增运算符
    100天精通Python(数据分析篇)——第49天:初识numpy模块
    UniPro助力半导体企业之低代码平台篇:高效协同快速响应
    尚医通 (十五) --------- 平台管理前端搭建
    面试:容器技术
    APISIX 中ETCD 的问题
  • 原文地址:https://blog.csdn.net/weixin_45817985/article/details/136678255