Java 设计模式——状态模式

目录

  • 1.概述
  • 2.结构
  • 3.案例实现
    • 3.1.抽象状态类
    • 3.2.具体状态类
    • 3.3.上下文类
    • 3.4.测试
  • 4.优缺点
  • 5.使用场景

1.概述

【例】通过按钮来控制一个电梯的状态,电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。其类图如下:
在这里插入图片描述
具体实现代码如下:

ILift.java

public interface ILift {

    //定义四个电梯状态的常量
    int OPENING_STATE = 1;
    int CLOSING_STATE = 2;
    int RUNNING_STATE = 3;
    int STOPPING_STATE = 4;

    //设置电梯状态的功能
    void setState(int state);

    //电梯操作功能
    void open();
    void close();
    void run();
    void stop();
}

Lift.java

//电梯类(ILift 接口的实现类)
public class Lift implements ILift {

    //声明一个记录当前电梯的状态
    private int state;

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

    public void open() {
        switch (state) { //当前电梯状态
            case OPENING_STATE :
                //什么事都不做
                break;
            case CLOSING_STATE :
                System.out.println("电梯打开了...");
                //设置当前电梯状态为开启状态
                setState(OPENING_STATE);
                break;
            case STOPPING_STATE :
                System.out.println("电梯打开了...");
                //设置当前电梯状态为开启状态
                setState(OPENING_STATE);
                break;
            case RUNNING_STATE :
                //什么事都不做
                break;
        }
    }

    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;
        }
    }

    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;
        }
    }

    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;
        }
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        //创建电梯对象
        Lift lift = new Lift();

        //设置当前电梯的状态
        lift.setState(ILift.OPENING_STATE);

        //打开
        lift.open();
        lift.close();
        lift.run();
        lift.stop();
    }
}

问题分析:

  • 使用了大量的 switch…case 这样的判断(换成 if…else 也是一样),使程序的可读性变差。
  • 扩展性很差,如果新加了断电的状态,则需要修改上面的判断逻辑。

(2)状态模式 (State Pattern) 是一种行为型设计模式,它允许在一个对象内部状态改变时改变它的行为。状态模式将状态封装成独立的类,并将动作委托给代表当前状态的对象,以此实现状态转换时的行为变化。使用状态模式,我们可以将状态转换规则封装在不同状态类中,让上下文对象从复杂的状态转换逻辑中解耦出来,同时利用多态特性让状态转换具有扩展性和灵活性。

2.结构

状态模式包含以下主要角色:

  • 上下文 (Context) 角色:是一个包含状态的对象,它在运行时根据不同的状态改变其行为。上下文对象持有一个对当前状态的引用,并将具体的操作委托给当前状态对象处理。
  • 抽象状态 (Abstract State) 角色:定义了一个公共的接口,用于封装不同状态的行为。可以是一个抽象类或接口,并声明了在不同状态下可能发生的方法。
  • 具体状态 (Concrete State) 角色:实现了抽象状态接口,并定义了在对应状态下具体的行为。每个具体状态类都封装了一种特定状态的行为。

3.案例实现

对上述电梯的案例使用状态模式进行改进。类图如下:
在这里插入图片描述
具体实现代码如下:

3.1.抽象状态类

LiftState.java()

//抽象状态类
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.2.具体状态类

OpeningState.java

//电梯开启状态类
public class OpeningState extends LiftState {
    //当前状态要执行的方法
    public void open() {
        System.out.println("电梯开启。。。");
    }

    public void close() {
        //修改状态
        super.context.setLiftState(Context.CLOSING_STATE);
        //调用当前状态中的context中的close方法
        super.context.close();
    }

    public void run() {
        //什么都不做
    }

    public void stop() {
        //什么都不做
    }
}

ClosingState.java

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

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

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

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

RunningState.java

//电梯运行状态类
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.STOPPING_STATE);
        super.context.stop();
    }
}

StoppingState.java

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

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

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

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

3.3.上下文类

Context.java

//环境角色类
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;
        //设置当前状态对象中的 Context 对象
        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();
    }
}

3.4.测试

Client.java

public class Client {
    public static void main(String[] args) {
        //创建环境角色对象
        Context context = new Context();
        //设置当前电梯装填
        context.setLiftState(new ClosingState());

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

4.优缺点

  • 优点:
    • 将状态封装成独立的类,使得状态转换的逻辑更加清晰,避免了大量的条件判断语句。
    • 将状态和状态切换的行为分离,使得状态转换的代码可扩展和可维护。增加新的状态类或修改现有的状态类时,对上下文对象和其他状态类都没有影响。
    • 增强了代码的可读性和可维护性。每个状态都有自己的类,使得代码具备了良好的结构化,并且易于理解和修改。
    • 通过引入多态性,可以方便地增加新的状态类,而不需要修改现有的代码。
  • 缺点:
    • 如果状态类很多,可能会导致类的数量庞大,增加了代码的复杂性。
    • 状态类的创建和销毁需要维护和管理,可能会增加系统的开销。
    • 当状态转换的逻辑较为简单时,使用状态模式可能会显得过于繁琐。

5.使用场景

(1)状态模式适用于以下情况:

  • 当一个对象的行为取决于其内部状态,并且在不同状态下需要有不同的行为时,可以使用状态模式。例如,订单状态的改变会导致不同的操作和行为,可以使用状态模式来管理订单的状态转换。
  • 当有大量的条件语句用于根据不同状态执行不同操作时,可以使用状态模式来简化代码并提高可维护性。
  • 当一个对象的行为需要根据多个条件进行判断,并且这些条件随着时间的推移可能会发生变化时,可以使用状态模式。通过将每个条件封装为一个状态类,可以方便地添加、修改或删除状态,而不会对其他部分产生影响。
  • 当需要在运行时动态改变对象的行为时,可以使用状态模式。通过改变对象的状态,可以改变其行为,而不需要修改对象的代码。

(2)总之,状态模式适用于需要根据对象的内部状态来改变其行为,并且希望将状态转换逻辑封装在独立的状态类中的情况。它提供了一种清晰、灵活和可扩展的方式来管理对象的状态和行为。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/122425.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

银行APP虚拟金额软件,建设农业工商邮政余额生成器,易语言开源版

用易语言开发了一个虚拟余额装逼软件,可以生成虚拟的余额截图,就是APP端的截图,用的画板组件,但是生成出来的图片是非常高清的,软件里面因为图片是缩放状态,所以看起来有点失真的感觉,生成图片的…

Mysql进阶-视图篇

介绍 视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。…

汇编-EQU伪指令(数值替换)

EQU伪指令将一个符号名称与一个整数表达式或一个任意文本相关联, 它有3种格式 在第一种格式中, expression必须是一个有效的整数表达式。在第二种格式中, symbol是一个已存在的符号名称, 已经用或EQU定义过。在第三种格式中&…

想水目标检测sci论文的同学看过来:第一个用于目标检测的扩散模型

目标检测新范式!在 COCO、CrowdHuman 和 LVIS上取得了良好的性能,尤其是跨不同场景的零样本迁移 本文提出了 DiffusionDet,这是一个新框架,它将目标检测制定为从噪声框到对象框的去噪扩散过程。 在训练阶段,目标框从真…

echart的tooltip显示不同的单位

效果 实现 在每个series中添加不同的 tooltip: { valueFormatter: function (value) { return value.toFixed(0) ‘A’; } }, 代码如下 var option {// grid: {// left: -13vw,//左边距72px// right: 32%,// bottom: 64%,// top:…

clickhouse通过java jdbc实现增删改查,保姆级教程

一、clickhouse是一款开源的用于在线分析处理查询(OLAP :Online Analytical Processing)MPP架构的列式存储数据库。 二、clickhouse可以做用户行为分析,流批一体 三、我们现在用java通过jdbc的方式来操作clickhouse 四、先安装clickhouse,安装资料自行…

通达OA V12版,引入thinkphp5.1框架,及获取session

通达OA V12版,引入thinkphp5.1框架 如下过程引入如下问题,按上述问题解决htmlentities(): charset cp936 not supported, assuming utf-8 内容绝对原创,希望对您有帮助。您的打赏,是让我持续更新的牛奶和面包 如下过程引入 在D:/…

有效降低数据库存储成本方案与实践 | 京东云技术团队

背景 随着平台的不断壮大,业务的不断发展,后端系统的数据量、存储所使用的硬件成本也逐年递增。从发展的眼光看,业务与系统要想健康的发展,成本增加的问题必须重视起来。目前业界普遍认同开源节流大方向,很多企业部门…

总结Kibana DevTools如何操作elasticsearch的常用语句

一、操作es的工具 ElasticSearch HeadKibana DevToolsElasticHQ 本文主要是总结Kibana DevTools操作es的语句。 二、搜索文档 1、根据ID查询单个记录 GET /course_idx/_doc/course:202、term 匹配"name"字段的值为"6789999"的文档 类似于sql语句中的等…

载波通讯电表的使用年限是多久?

随着科技的飞速发展,智能家居、物联网等概念逐渐深入人心,载波通讯电表作为一种新型的智能电表,凭借其低功耗、高可靠性、远程通讯等优点,广泛应用于居民用电、工业生产等领域。那么,载波通讯电表的使用年限是多久呢&a…

vTESTstudio的使用

vTESTstudio介绍 vTESTstudio是Vector公司专为ECU自动化测试而研发的测试用例编写软件,可用于从模型测试到系统确认的所有开发阶段。它集成了多种用例编辑方法,能有效提高测试人员对测试设计的效率、具有可复用性等优点。它支持Python作为测试编程语言&…

vtk夹角计算控件

开发环境: Windows 11 家庭中文版Microsoft Visual Studio Community 2019VTK-9.3.0.rc0vtk-example参考代码目的:学习与总结 demo解决问题:renderWindow中创建一个夹角测量控件,通过三个点确定一个夹角 典型的控件类继承关系&am…

第134届广交会圆满落幕,百华鞋业不负众望,载誉收官!

第134届广交会线下展在广州圆满落幕,交出了亮眼的成绩单。相比往届,本届广交会展览总面积扩至155万平方米,展位总数7.4万个,展区总数55个,参展企业28533家,均创历史新高。据央媒等相关报道,本届…

Ubuntu LTS 坚持 10 年更新不动摇

导读Linux 内核开发者 Jonathan Corbet 此前在欧洲开源峰会上宣布,LTS 内核的支持时间将从六年缩短至两年,原因在于缺乏使用和缺乏支持。稳定版内核维护者 Greg Kroah-Hartman 也表示 “没人用 LTS 内核”。 近日,Ubuntu 开发商 Canonical 发…

COOHOM通过采用亚马逊云科“专库专用”的方式,为云原生的构建提供稳定的数据支撑

全球化浪潮下,面对全球化业务发展带来的新需求与新挑战,越来越多的企业开启了云原生构建旅程,以推动业务系统快速迭代,为国际业务的拓展打下坚实的基础。COOHOM是杭州群核信息技术有限公司旗下的国际化品牌。为全球企业和个人提供…

taro(踩坑) npm run dev:weapp 微信小程序开发者工具预览报错

控制台报错信息: VM72:9 app.js错误: Error: module vendors-node_modules_taro_weapp_prebundle_chunk-JUEIR267_js.js is not defined, require args is ./vendors-node_modules_taro_weapp_prebundle_chunk-JUEIR267_js.js 环境: node 版本&#x…

KubeSphere v3.4.0 部署K8S Docker + Prometheus + grafana

KubeSphere v3.4.0 部署K8S 1、整体思路2、修改linux主机名3、 离线安装3.1 问题列表3.2 执行命令成功列表 1、整体思路 将KubeSphere v3.4.0 安装包传输到其中一台机器修改Linux主机名(选取3台,修改为master01、master02、master03)安装官方…

电脑监控软件怎样防止数据泄露丨实战干货分享

电脑监控软件大家应该都不陌生吧,原本它是国外的产物,只不过在02年的时候,在国内出现。 一开始它的功能也仅限于监控应该的电脑画面,但是随着电脑问题逐渐增加,其他和公司企业管理、员工管理、防止数据泄露的功能就都…

仿mudou库one thread one loop式并发服务器

目录 1.实现目标 2.HTTP服务器 实现高性能服务器-Reactor模型 模块划分 SERVER模块: HTTP协议模块: 3.项目中的子功能 秒级定时任务实现 时间轮实现 正则库的简单使用 通⽤类型any类型的实现 4.SERVER服务器实现 日志宏的封装 缓冲区Buffer…

vscode中 vue3+ts 项目的提示失效,volar插件失效问题解决方案

文章目录 前情提要bug回顾解决方案最后 前情提要 说起来很耻辱,从mac环境换到window环境,vscode的配置都是云端更新过来的,应该是一切正常才对,奇怪的是我的项目环境出现问题了,关于组件的ts和追踪都没有效果&#xff…
最新文章