设计模式-状态模式

一、什么是状态模式

状态模式是一种行为设计模式,使用状态模式,能够在一个对象内部状态变化时改变它的行为,使其看起来就像改变自身的类一样。
核心点:区分事物内部的状态,把事物的每种状态都封装成单独的类,跟此状态有关的行为都被封装在这个类的内部,当对象的状态改变时,它的行为也会随之改变。

二、状态模式的主要构成

  1. 环境类(Context)角色:也叫上下文,定义类客户端需要的接口,维护了一个当前状态,并将状态相关的操作委托给当前的状态对象来处理;
  2. 抽象状态(State)角色:定义一个状态接口,用于封装状态所对应的行为;
  3. 具体状态(Concrete State)角色:实现抽象状态对应的行为,用于切换状态

三、状态模式代码示例

假如有一个抽奖奖励场景,奖励存在多种状态,分别为未激活状态、已激活状态、已暂停状态和已结束状态,各个状态之间会根据相关方法或者时间触发到转换到其他状态,状态转换规则如下图所示

处于未激活状态 ,它会根据开始时间自动激活奖励,转移到已激活状态 ,处于已激活状态的奖励,管理员可以将抽奖活动进行暂停,此时奖励会进入已暂停状态 ,处于已暂停状态的奖励,又可以手动开启,进入已激活状态;处于已激活状态,会随着奖品的发放完成,进入到已结束状态,在不同的状态下,奖励对应的行为也是不同的。

假如没有使用状态模式,我们通常的做法是使用多个条件运算符if else或switch来实现,代码示例如下:

java 复制代码
public class Reward {
    private enum State {
        INACTIVE, ACTIVE, PAUSED, FINISHED
    }

    private State currentState;

    public Reward() {
        currentState = State.INACTIVE; // 初始状态为未激活
    }

    public void activate() {
        switch (currentState) {
            case INACTIVE:
                System.out.println("奖励已激活");
                currentState = State.ACTIVE;
                break;
            case ACTIVE:
                System.out.println("奖励已激活,无需再次激活");
                break;
            case PAUSED:
                System.out.println("奖励已激活");
                break;
            case FINISHED:
                System.out.println("奖励已结束,无法激活");
                break;
        }
    }

    public void pause() {
        switch (currentState) {
            case INACTIVE:
                System.out.println("奖励未激活,不能暂停");
                break;
            case ACTIVE:
                System.out.println("奖励已暂停");
                currentState = State.PAUSED;
                break;
            case PAUSED:
                System.out.println("奖励已暂停,无需再次暂停");
                break;
            case FINISHED:
                System.out.println("奖励已结束,无法暂停");
                break;
        }
    }

    public void finish() {
        switch (currentState) {
            case INACTIVE:
                System.out.println("奖励已结束");
                break;
            case ACTIVE:
                System.out.println("奖励已结束");
                currentState = State.FINISHED;
                break;
            case PAUSED:
                System.out.println("奖励已结束");
                currentState = State.FINISHED;
                break;
            case FINISHED:
                System.out.println("奖励已结束,无需再次结束");
                break;
        }
    }

    public static void main(String[] args) {
        Reward reward = new Reward();

        // 未激活状态
        reward.activate();
        reward.pause();
        reward.finish();

        // 激活状态
        reward.activate();
        reward.pause();
        reward.finish();

        // 暂停状态
        reward.pause();
        reward.finish();

        // 结束状态
        reward.finish();
        reward.activate();
        reward.pause();
        reward.finish();
    }
}

存在的问题:由于我们预先定义了枚举类的一些状态,后面会随着业务规则变动,又会增加状态以及状态之间的转换规则,则传统的if else结构,显得扩展性不足,代码会变得臃肿不好维护。

使用状态模式实现如下:

java 复制代码
public interface RewardState {
    void activate();
    void pause();
    void finish();
}
public class InactiveState implements RewardState{
    @Override
    public void activate() {
        System.out.println("奖励已激活");
    }
    @Override
    public void pause() {
        System.out.println("奖励未激活,不能暂停");
    }
    @Override
    public void finish() {
        System.out.println("奖励未激活,不能结束");
    }
}
public class ActiveState implements RewardState{
    @Override
    public void activate() {
        System.out.println("奖励已激活,无需再次激活");
    }
    @Override
    public void pause() {
        System.out.println("奖励已暂停");
    }
    @Override
    public void finish() {
        System.out.println("奖励已结束");
    }
}
public class PausedState implements RewardState{

    @Override
    public void activate() {
        System.out.println("奖励已激活");
    }
    @Override
    public void pause() {
        System.out.println("奖励已暂停,无需再次暂停");
    }
    @Override
    public void finish() {
        System.out.println("奖励已结束");
    }
}
public class FinishedState implements RewardState{
    @Override
    public void activate() {
        System.out.println("奖励已结束,无法再次激活");
    }
    @Override
    public void pause() {
        System.out.println("奖励已结束,无法暂停");
    }
    @Override
    public void finish() {
        System.out.println("奖励已结束,无需再次结束");
    }
}
public class RewardContext {
    private RewardState state;

    public RewardContext() {
        state = new InactiveState(); // 初始状态为未激活
    }

    public void setState(RewardState state) {
        this.state = state;
    }

    public void activate() {
        state.activate();
    }

    public void pause() {
        state.pause();
    }

    public void finish() {
        state.finish();
    }
}

// Demo测试
public class Main {
    public static void main(String[] args) {
        RewardContext context = new RewardContext();

        // 未激活状态
        context.activate();
        context.pause();
        context.finish();

        // 激活状态
        context.setState(new ActiveState());
        context.activate();
        context.pause();
        context.finish();

        // 暂停状态
        context.setState(new PausedState());
        context.activate();
        context.pause();
        context.finish();

        // 结束状态
        context.setState(new FinishedState());
        context.activate();
        context.pause();
        context.finish();
    }
}

执行结果:

四、状态模式的应用

  1. 订单处理系统,订单可以处于不同的状态,如已下单、待支付、已支付、待发货、已发货、已完成等。状态模式可以用来管理订单之间状态的转换和对应的执行的操作。
  2. 工作流引擎,在工作流管理中,任务可以处于不同的状态,如待处理、处理中、处理完成等等。
  3. 游戏角色状态,游戏中的人物角色可以处于不同的状态,例如站立状态、行走状态、跑动状态、攻击状态等
  4. 电梯控制系统,电梯可以处于不同的状态,例如停止状态、运行状态、上行、下行、故障等等。

五、状态模式优缺点

优点:

  1. 代码可读性好,状态之间的转换封装到了具体类中,可以清晰管理状态之间的转换;
  2. 可维护性好,扩展性强,如添加和删除状态比较方便,不会影响原有状态类的代码;
  3. 符合开闭原则: 状态模式使得状态类可以独立变化和扩展,符合开闭原则,对修改封闭,对扩展开放。

缺点:

  1. . 增加代码中对象和类的个数,增加了系统实现的复杂性;

  2. 状态间的转换: 状态之间的转换可能比较复杂,需要仔细设计状态之间的转换关系;

  3. 状态模式的适用场景有限: 如果一个对象的状态很少改变,或者状态转换比较简单,使用状态模式可能会显得过于繁琐。

参考:订单及其状态机的设计实现

相关推荐
云心似我心^o^4053 分钟前
使用POI和EasyExcel使用导入
java
我是大头鸟13 分钟前
SpringMVC 使用thymeleaf 进行数据展示
java·springmvc·thymeleaf
小刘|15 分钟前
JVM 自动内存管理
java·jvm·算法
2401_8370885026 分钟前
eclipse怎么导入junit4
java·junit·eclipse
幼儿园口算大王27 分钟前
Spring反射机制
java·spring·反射
北漂老男孩42 分钟前
设计模式全解析:23种经典设计模式及其应用
单例模式·设计模式
purrrew1 小时前
【JAVA ee初阶】多线程(3)
java·开发语言
每次的天空1 小时前
Android学习总结之Java篇(一)
android·java·学习
尤物程序猿2 小时前
【2025最新Java面试八股】如何在Spring启动过程中做缓存预热?
java·缓存·面试
春眠不觉晓♞2 小时前
使用多线程快速向Excel中快速插入一万条数据案例
java·学习·excel