状态模式【State Pattern】,什么是状态模式?主要角色?主要作用?优缺点?适用场景?状态模式实现案例?

目录


设计模式专栏目录(点击进入…)



什么是状态模式?

状态模式(State Pattern)是一种行为设计模式,允许一个对象在其内部状态改变时改变其行为。状态模式将一个对象的状态和与之相关的行为分离开来,使得对象在不同状态下可以有不同的行为表现,从而使得代码更加清晰和易于维护。


状态模式主要角色

(1)Context(上下文)

维护一个当前状态的实例,并定义一个接口,允许客户端通过这个接口来变更状态。

(2)State(状态)

定义一个接口,用于封装与上下文的一个特定状态相关的行为。

(3)ConcreteState(具体状态)

每个具体状态实现状态接口,定义在这个状态下具体的行为。


状态模式的主要作用

(1)将状态逻辑集中管理

将状态相关的行为集中在状态类中,使得状态的添加和修改更加方便。

(2)避免使用大量的条件判断

通过将状态和行为封装到独立的状态类中,减少了复杂的条件判断。

(3)增加系统的灵活性

状态模式使得系统在运行时可以方便地切换状态,从而改变对象的行为。


状态模式的优缺点

优点

(1)清晰的结构

将状态和行为分离,使得系统的结构更加清晰。

(2)易于扩展

增加新的状态只需要增加一个新的状态类,而不需要修改现有代码。

(3)减少条件判断

通过状态模式避免了在上下文类中使用大量的条件判断,使代码更加简洁。

缺点

(1)类数量增加

每个状态都需要一个独立的类,这会导致类的数量增加。

(2)复杂性

在一些简单的应用场景下,状态模式可能会引入不必要的复杂性。


状态模式适用场景

(1)状态行为变化复杂的对象

如:状态机、订单处理系统等。

(2)行为依赖于状态的对象

如:在工作流系统中,根据不同的流程阶段改变行为。

(3)需要在运行时动态改变行为的对象

如:文档编辑器中不同的编辑状态。


状态模式实现案例

我们每天都在乘电梯,那来看看电梯有哪些动作(映射到 Java 中就是有多少方法):开门、关门、
运行、停止,就这四个动作。好,就用程序来实现一下电梯的动作。

先看类图设计:
在这里插入图片描述

电梯门可以打开,但不是随时都可以开,是有前提条件的,不可能电梯在运行的时候突然开门吧?电梯也不会出现停止了但是不开门的情况吧!那要是有也是事故嘛,再仔细想想,电梯的这四个动作的执行都是有前置条件;具体点,是在特定状态下才能做特定事,那来分析一下电梯有什么那些特定状态:
门敞状态—按了电梯上下按钮,电梯门开,这中间有 5 秒的时间(当然你也可以用身体挡住电梯门,那就不是 5 秒了),那就是门敞状态;在这个状态下电梯只能做的动作是关门动作,做别的动作?那就危险喽
门闭状态—电梯门关闭了,在这个状态下,可以进行的动作是:开门(我不想坐电梯了)、停止(忘记按路层号了)、运行
运行状态—电梯正在跑,上下窜,在这个状态下,电梯只能做的是停止;
停止状态—电梯停止不动,在这个状态下,电梯有两个可选动作:继续运行和开门动作;

用一张表来表示电梯状态和动作之间的关系:
电梯状态和动作对应表(×表示不允许,√表示允许动作)

开门(open)关门(close)运行(run)停止(stop)
门敞状态×××
门闭状态×
运行状态×××
停止状态××

1、定义上下文

package com.uhhe.common.design.state;

/**
 * 上下文
 * 
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/2 19:56
 */
public class Context {

    /**
     * 定义出所有的电梯状态
     */
    public final static OpeningState OPENING_STATE = new OpeningState();        // 门敞状态
    public final static ClosingState CLOSING_STATE = new ClosingState();        // 门闭状态
    public final static RunningState RUNNING_STATE = new RunningState();        // 运行状态
    public final static StoppingState STOPPING_STATE = new StoppingState();     // 停止状态

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

    public LiftState getLiftState() {
        return 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();
    }

}

2、状态抽象类(State)

package com.uhhe.common.design.state;

/**
 * 状态抽象
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/2 19:54
 */
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();

}

3、状态实现类(ConcreteState,门敞状态、门闭状态、运行状态、停止状态)

门敞状态

package com.uhhe.common.design.state;

/**
 * 门敞状态:在电梯门开启的状态下能做什么事情
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/2 19:55
 */
public class OpeningState extends LiftState {

    @Override
    public void close() {
        //状态修改
        super.context.setLiftState(Context.CLOSING_STATE);
        //动作委托为CloseState来执行
        super.context.getLiftState().close();
    }

    @Override
    public void open() {
        System.out.println("电梯门开启...");
    }

    @Override
    public void run() {
        // 门开着电梯就想跑,这电梯,吓死你!
    }

    @Override
    public void stop() {
        // 开门还不停止?
    }

}

门闭状态

package com.uhhe.common.design.state;

/**
 * 门闭状态:电梯门关闭以后,电梯可以做哪些事情
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/2 19:57
 */
public class ClosingState extends LiftState {

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

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

    @Override
    public void run() {
        // 电梯门关了就跑,这是再正常不过了
        // 设置为运行状态
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.getLiftState().run();
    }

    @Override
    public void stop() {
        // 电梯门关着,我就不按楼层
        // 设置为停止状态;
        super.context.setLiftState(Context.STOPPING_STATE);
        super.context.getLiftState().stop();
    }

}

运行状态

package com.uhhe.common.design.state;

/**
 * 运行状态:电梯在运行状态下能做哪些动作
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/2 19:58
 */
public class RunningState extends LiftState {

    @Override
    public void close() {
        // 电梯门关闭?这是肯定了
        // do nothing
    }

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

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

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

}

停止状态

package com.uhhe.common.design.state;

/**
 * 停止状态:在停止状态下能做什么事情
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/2 19:59
 */
public class StoppingState extends LiftState {

    @Override
    public void close() {
        // 停止状态关门?电梯门本来就是关着的!
        // do nothing;
    }

    @Override
    public void open() {
        // 停止状态,开门,那是要的!
        super.context.setLiftState(Context.OPENING_STATE);
        super.context.getLiftState().open();
    }

    @Override
    public void run() {
        // 停止状态再跑起来,正常的很
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.getLiftState().run();
    }

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

}

4、模拟电梯的动作

package com.uhhe.common.design.state;

/**
 * 模拟电梯的动作
 *
 * @author nizhihao
 * @version 1.0.0
 * @date 2023/3/2 20:01
 */
public class Client {

    /**
     * 状态模式【State Pattern】
     * <p>
     * 什么是状态模式呢?
     * 当一个对象内在状态改变时允许其改变行为,这个对象看起来像是改变了其类。
     * 也就是说状态模式封装的非常好,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。
     * <p>
     * 状态模式中有什么优点呢?
     * 首先是避免了过多的 swith/case 或者 if..else 语句的使用,避免了程序的复杂性;其次是很好的使用体现了开闭原则和单一职责原则,
     * 每个状态都是一个子类,你要增加状态就增加子类,你要修改状态,你只修改一个子类就可以了;最后一个好处就是封装性非常好,这也是状态模式的基本要求,
     * 状态变换放置到了类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。
     * <p>
     * 状态模式只有一个缺点,子类会太多,也就是类膨胀,你想一个事物有七八、十来个状态也不稀奇,如果完全使用状态模式就会有太多的子类,不好管理,
     * 这个需要大家在项目自己衡量。其实有很大方式解决这个状态问题
     * 比如: 在数据库中建立一个状态表,然后根据状态执行相应的操作,这个也不复杂,看大家的习惯和嗜好了。
     * <p>
     * 状态模式使用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说行为是受状态约束的情况下可以使用状态模式,
     * 而且状态模式使用时对象的状态最好不要超过五个,防止你写子类写疯掉。
     */
    public static void main(String[] args) {
        Context context = new Context();
        context.setLiftState(new ClosingState());

        context.open();
        context.close();
        context.run();
        context.stop();
    }

}
  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值