设计模式-状态模式

一、什么是状态模式

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

二、状态模式的主要构成

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

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

相关推荐
一只爱打拳的程序猿9 分钟前
【Spring】更加简单的将对象存入Spring中并使用
java·后端·spring
杨荧11 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
minDuck13 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
为将者,自当识天晓地。31 分钟前
c++多线程
java·开发语言
daqinzl39 分钟前
java获取机器ip、mac
java·mac·ip
激流丶1 小时前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue1 小时前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
让学习成为一种生活方式1 小时前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画1 小时前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生2 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法