【设计模式】【行为型模式】状态模式(State)

简介: 一、入门 什么是状态模式? 状态模式(State Pattern)是一种行为设计模式,允许对象在其内部状态改变时改变其行为,使其看起来像是改变了类。状态模式的核心思想是将对象的状态封装成独立的类,并将

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是状态模式?

状态模式(State Pattern)是一种行为设计模式,允许对象在其内部状态改变时改变其行为,使其看起来像是改变了类。状态模式的核心思想是将对象的状态封装成独立的类,并将行为委托给代表当前状态的对象。

为什么需要状态模式?

有这样一个场景,如果不用状态模式,那么电题的每一个动作(open、close、stop、run),都会先对状态进行判断,再决定能否执行(比如运行中的电梯是不能开门的),类图如下所示。

image.png

public class Lift implements ILift {
   
    private int state;

    @Override
    public void setState(int state) {
   
        this.state = state;
    }

    //执行关门动作
    @Override
    public void close() {
   
        switch (this.state) {
   
            case OPENING_STATE:
                System.out.println("电梯关门了。。。");//只有开门状态可以关闭电梯门,可以对应电梯状态表来看
                this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了
                break;
            case CLOSING_STATE:
                //do nothing //已经是关门状态,不能关门
                break;
            case RUNNING_STATE:
                //do nothing //运行时电梯门是关着的,不能关门
                break;
            case STOPPING_STATE:
                //do nothing //停止时电梯也是关着的,不能关门
                break;
        }
    }

    //执行开门动作
    @Override
    public void open() {
   
        switch (this.state) {
   
            case OPENING_STATE://门已经开了,不能再开门了
                //do nothing
                break;
            case CLOSING_STATE://关门状态,门打开:
                System.out.println("电梯门打开了。。。");
                this.setState(OPENING_STATE);
                break;
            case RUNNING_STATE:
                //do nothing 运行时电梯不能开门
                break;
            case STOPPING_STATE:
                System.out.println("电梯门开了。。。");//电梯停了,可以开门了
                this.setState(OPENING_STATE);
                break;
        }
    }

    //执行运行动作
    @Override
    public void run() {
   
        switch (this.state) {
   
            case OPENING_STATE://电梯不能开着门就走
                //do nothing
                break;
            case CLOSING_STATE://门关了,可以运行了
                System.out.println("电梯开始运行了。。。");
                this.setState(RUNNING_STATE);//现在是运行状态
                break;
            case RUNNING_STATE:
                //do nothing 已经是运行状态了
                break;
            case STOPPING_STATE:
                System.out.println("电梯开始运行了。。。");
                this.setState(RUNNING_STATE);
                break;
        }
    }

    //执行停止动作
    @Override
    public void stop() {
   
        switch (this.state) {
   
            case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)
                //do nothing
                break;
            case CLOSING_STATE://关门时才可以停止
                System.out.println("电梯停止了。。。");
                this.setState(STOPPING_STATE);
                break;
            case RUNNING_STATE://运行时当然可以停止了
                System.out.println("电梯停止了。。。");
                this.setState(STOPPING_STATE);
                break;
            case STOPPING_STATE:
                //do nothing
                break;
        }
    }
}
AI 代码解读

在没有使用状态模式的情况下,通常会使用大量的if-elseswitch-case语句来处理对象在不同状态下的行为。这种方式会带来以下问题:

  • 代码臃肿且难以维护:当状态数量增加时,if-elseswitch-case语句会变得非常冗长。每次新增状态或修改状态转换逻辑时,都需要修改这些条件语句,容易引入错误。
  • 违反开闭原则:开闭原则要求软件实体(类、模块、函数等)对扩展开放,对修改关闭。使用条件语句时,新增状态或修改行为需要修改现有代码,而不是扩展。
  • 状态和行为耦合:状态和行为逻辑通常混杂在一起,导致代码难以理解和测试。如果需要复用某些状态的行为,很难将其提取出来。
  • 难以扩展:当状态转换逻辑变得复杂时(例如,某些状态的下一个状态有多个分支),条件语句会变得更加混乱。新增状态或修改状态转换逻辑时,可能会影响其他状态的逻辑。

怎样实现状态模式?

状态模式的组成

  • Context(上下文):它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  • State(状态):定义一个接口,封装与Context的特定状态相关的行为。
  • ConcreteState(具体状态):实现State接口,定义与Context的某个状态相关的行为。

【案例】 电梯状态改进版
image.png

State(状态)LiftState

//抽象状态类
public abstract class LiftState {
   
    //定义一个环境角色,也就是封装状态的变化引起的功能变化
    protected Context context;

    public void setContext(Context context) {
   
        this.context = context;
    }

    //电梯开门动作
    public abstract void open();

    //电梯关门动作
    public abstract void close();

    //电梯运行动作
    public abstract void run();

    //电梯停止动作
    public abstract void stop();
}
AI 代码解读

ConcreteState(具体状态)OpenningState类、RunningState类、StoppingState类和ClosingState

//开启状态
public class OpenningState extends LiftState {
   

    //开启当然可以关闭了,我就想测试一下电梯门开关功能
    @Override
    public void open() {
   
        System.out.println("电梯门开启...");
    }

    @Override
    public void close() {
   
        //状态修改
        super.context.setLiftState(Context.closeingState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().close();
    }

    //电梯门不能开着就跑,这里什么也不做
    @Override
    public void run() {
   
        //do nothing
    }

    //开门状态已经是停止的了
    @Override
    public void stop() {
   
        //do nothing
    }
}

//运行状态
public class RunningState extends LiftState {
   

    //运行的时候开电梯门?你疯了!电梯不会给你开的
    @Override
    public void open() {
   
        //do nothing
    }

    //电梯门关闭?这是肯定了
    @Override
    public void close() {
   //虽然可以关门,但这个动作不归我执行
        //do nothing
    }

    //这是在运行状态下要实现的方法
    @Override
    public void run() {
   
        System.out.println("电梯正在运行...");
    }

    //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
    @Override
    public void stop() {
   
        super.context.setLiftState(Context.stoppingState);
        super.context.stop();
    }
}

//停止状态
public class StoppingState extends LiftState {
   

    //停止状态,开门,那是要的!
    @Override
    public void open() {
   
        //状态修改
        super.context.setLiftState(Context.openningState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().open();
    }

    @Override
    public void close() {
   //虽然可以关门,但这个动作不归我执行
        //状态修改
        super.context.setLiftState(Context.closeingState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().close();
    }

    //停止状态再跑起来,正常的很
    @Override
    public void run() {
   
        //状态修改
        super.context.setLiftState(Context.runningState);
        //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
        super.context.getLiftState().run();
    }

    //停止状态是怎么发生的呢?当然是停止方法执行了
    @Override
    public void stop() {
   
        System.out.println("电梯停止了...");
    }
}

//关闭状态
public class ClosingState extends LiftState {
   

    @Override
    //电梯门关闭,这是关闭状态要实现的动作
    public void close() {
   
        System.out.println("电梯门关闭...");
    }

    //电梯门关了再打开,逗你玩呢,那这个允许呀
    @Override
    public void open() {
   
        super.context.setLiftState(Context.openningState);
        super.context.open();
    }


    //电梯门关了就跑,这是再正常不过了
    @Override
    public void run() {
   
        super.context.setLiftState(Context.runningState);
        super.context.run();
    }

    //电梯门关着,我就不按楼层
    @Override
    public void stop() {
   
        super.context.setLiftState(Context.stoppingState);
        super.context.stop();
    }
}
AI 代码解读

Context(上下文)Context

//环境角色
public class Context {
   
    //定义出所有的电梯状态
    public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭
    public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门
    public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止
    public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行


    //定义一个当前电梯状态
    private LiftState liftState;

    public LiftState getLiftState() {
   
        return this.liftState;
    }

    public void setLiftState(LiftState liftState) {
   
        //当前环境改变
        this.liftState = liftState;
        //把当前的环境通知到各个实现类中
        this.liftState.setContext(this);
    }

    public void open() {
   
        this.liftState.open();
    }

    public void close() {
   
        this.liftState.close();
    }

    public void run() {
   
        this.liftState.run();
    }

    public void stop() {
   
        this.liftState.stop();
    }
}
AI 代码解读

测试类

//测试类
public class Client {
   
    public static void main(String[] args) {
   
        Context context = new Context();
        context.setLiftState(new ClosingState());

        context.open();
        context.close();
        context.run();
        context.stop();
    }
}
AI 代码解读

二、状态模式在源码中的运用

Java线程管理

Java 的线程(Thread)生命周期就是一个典型的状态模式应用。线程的状态包括NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED虽然 Java 线程的状态管理没有直接使用状态模式的设计,但其设计思想与状态模式非常相似。
线程状态转换:

  • NEW -> RUNNABLE(调用 start() 方法)
  • RUNNABLE -> BLOCKED(等待锁)
  • RUNNABLE -> WAITING(调用 wait() 方法)
  • RUNNABLE -> TIMED_WAITING(调用 sleep()join() 方法)
  • RUNNABLE -> TERMINATED(线程执行完毕)

如果使用状态模式来实现线程状态管理,可以这样设计:

interface ThreadState {
   
    void start(ThreadContext context);
    void run(ThreadContext context);
    void block(ThreadContext context);
    void terminate(ThreadContext context);
}

class NewState implements ThreadState {
   
    @Override
    public void start(ThreadContext context) {
   
        context.setState(new RunnableState());
    }

    @Override
    public void run(ThreadContext context) {
   
        throw new IllegalStateException("线程未启动,无法运行。");
    }

    @Override
    public void block(ThreadContext context) {
   
        throw new IllegalStateException("线程未启动,无法阻塞。");
    }

    @Override
    public void terminate(ThreadContext context) {
   
        throw new IllegalStateException("线程未启动,无法终止。");
    }
}

class RunnableState implements ThreadState {
   
    @Override
    public void start(ThreadContext context) {
   
        throw new IllegalStateException("线程已启动,无需再次启动。");
    }

    @Override
    public void run(ThreadContext context) {
   
        System.out.println("线程正在运行...");
    }

    @Override
    public void block(ThreadContext context) {
   
        context.setState(new BlockedState());
    }

    @Override
    public void terminate(ThreadContext context) {
   
        context.setState(new TerminatedState());
    }
}

class ThreadContext {
   
    private ThreadState state;

    public ThreadContext() {
   
        this.state = new NewState();
    }

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

    public void start() {
   
        state.start(this);
    }

    public void run() {
   
        state.run(this);
    }

    public void block() {
   
        state.block(this);
    }

    public void terminate() {
   
        state.terminate(this);
    }
}
AI 代码解读

Spring 状态机(Spring State Machine)

Spring 提供了一个状态机框架(Spring State Machine),用于管理复杂的状态转换逻辑。它基于状态模式设计,支持状态、事件、转换等概念。

@Configuration
@EnableStateMachine
public class StateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
   

    @Override
    public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
   
        states
            .withStates()
            .initial("SI") // 初始状态
            .state("S1")  // 状态 S1
            .state("S2");  // 状态 S2
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
   
        transitions
            .withExternal()
            .source("SI").target("S1").event("E1") // 事件 E1 触发 SI -> S1
            .and()
            .withExternal()
            .source("S1").target("S2").event("E2"); // 事件 E2 触发 S1 -> S2
    }
}
AI 代码解读

在这个例子中,Spring State Machine 使用状态模式来管理状态和事件驱动的转换。

三、总结

状态模式的优点

  • 消除复杂的条件语句:状态模式将状态相关的行为分散到各个状态类中,避免了大量的if-elseswitch-case语句,使代码更加清晰。

  • 符合开闭原则:新增状态时,只需添加新的状态类,而无需修改现有代码。修改某个状态的行为时,只需修改对应的状态类,不会影响其他状态。

  • 提高代码的可读性和可维护性:状态和行为被封装在独立的类中,代码结构更加清晰。每个状态类的职责单一,易于理解和测试。

  • 简化上下文逻辑:上下文(Context)只需维护当前状态的引用,并将行为委托给状态对象,逻辑更加简洁。

  • 支持复杂的状态转换:状态模式可以轻松处理多分支的状态转换逻辑(例如,一个状态的下一个状态有多个可能)。

状态模式的缺点

  • 增加类的数量:每个状态都需要一个单独的类,可能会导致类的数量增加,代码结构变得复杂。
  • 状态转换逻辑分散:状态转换逻辑分散在各个状态类中,可能会导致状态转换规则不够直观。
  • 不适合简单的状态管理:如果状态数量很少且状态转换逻辑简单,使用状态模式可能会显得过度设计。

状态模式的使用场景

  • 对象的行为依赖于其状态,且状态数量较多:例如,订单系统(新建、已支付、已发货、已完成等状态)。
  • 状态转换逻辑复杂:例如,游戏角色的状态(站立、跑步、跳跃、攻击等),每个状态的行为和转换条件不同。
  • 需要避免大量的条件语句:例如,线程状态管理(新建、运行、阻塞、终止等),使用状态模式可以避免复杂的条件判断。
  • 状态和行为需要动态切换:例如,TCP 连接状态(建立连接、数据传输、关闭连接等),状态模式可以灵活地管理状态转换。
  • 需要支持分层状态或子状态:例如,工作流引擎中的状态(主状态和子状态),状态模式可以很好地支持分层状态管理。

参考

黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili

flzjkl
+关注
目录
打赏
0
16
16
1
149
分享
相关文章
【设计模式】【行为型模式】备忘录模式(Memento)
一、入门 什么是备忘录模式? 备忘录模式(Memento Pattern)是一种行为设计模式,用于在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在需要时恢复该状态。它通常用于实现撤销操作
112 8
【设计模式】【行为型模式】职责链模式(Chain of Responsibility)
一、入门 什么是职责链模式? 职责链模式是一种行为设计模式,它允许你将请求沿着一条链传递,直到有对象处理它为止。每个对象都有机会处理请求,或者将其传递给链中的下一个对象。 为什么需要职责链模式? 使用
136 16
【设计模式】【行为型模式】解释器模式(Interpreter)
一、入门 什么是解释器模式? 解释器模式(Interpreter Pattern)是一种行为设计模式,用于定义语言的语法表示,并提供一个解释器来处理该语法。它通常用于需要解释和执行特定语言或表达式的场
80 11
|
3月前
|
【设计模式】【行为型模式】访问者模式(Visitor)
一、入门 什么是访问者模式? 访问者模式(Visitor Pattern)是一种行为设计模式,允许你将算法与对象结构分离。通过这种方式,可以在不改变对象结构的情况下,向对象结构中的元素添加新的操作。
115 10
【设计模式】【行为型模式】迭代器模式(Iterator)
一、入门 什么是迭代器模式? 迭代器模式(Iterator Pattern)是一种行为设计模式,它提供了一种顺序访问聚合对象中元素的方法,而不需要暴露其底层表示。迭代器模式将遍历逻辑从聚合对象中分离出
97 11
【设计模式】【行为型模式】中介者模式(Mediator)
一、入门 什么是中介者模式? 中介者模式(Mediator Pattern)是一种行为设计模式,旨在减少对象之间的直接依赖,通过引入一个中介者对象来协调多个对象之间的交互。这种模式特别适用于对象间存在
97 9
【设计模式】【行为型模式】观察者模式(Observer)
一、入门 什么是观察者模式? 观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。
185 9
【设计模式】【行为型模式】命令模式(Command)
一、入门 什么是命令模式? 命令模式是一种行为设计模式,它将请求或操作封装为对象,从而使你可以用不同的请求对客户进行参数化,并支持请求的排队、记录、撤销等操作。 命令模式的核心是将“请求”封装为独立的
149 15
【设计模式】【行为型模式】策略模式(Strategy)
一、入门 什么是策略模式? 策略模式是一种行为设计模式,允许在运行时选择算法或行为。它将算法封装在独立的类中,使得它们可以互换,而不影响客户端代码。 为什么需要策略模式? 策略模式的主要目的是解决算法
86 14
【设计模式】【行为型模式】模板方法模式(Template Method)
一、入门 1.1、什么是模板方法模式? 模板模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的框架,并允许子类在不改变算法结构的情况下重新定义算法的某些步骤。
97 13
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问