设计模式-状态模式

一、什么是状态模式

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

二、状态模式的主要构成

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

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

相关推荐
Amarantine、沐风倩✨33 分钟前
设计一个监控摄像头物联网IOT(webRTC、音视频、文件存储)
java·物联网·音视频·webrtc·html5·视频编解码·七牛云存储
路在脚下@2 小时前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
森屿Serien2 小时前
Spring Boot常用注解
java·spring boot·后端
Damon_X2 小时前
桥接模式(Bridge Pattern)
设计模式·桥接模式
苹果醋33 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader3 小时前
深入解析 Apache APISIX
java·apache
菠萝蚊鸭3 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪4 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0074 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生4 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl