Java 设计模式——状态模式

news2024/11/29 14:55:19

目录

  • 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.coloradmin.cn/o/1176640.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

劲升逻辑与安必快、鹏海运于进博会签署合作协议,助力大湾区外贸高质量发展

新中经贸与投资论坛签约现场 中国上海,2023 年 11 月 6 日——第六届进博会期间,由新加坡工商联合总会主办的新中经贸与投资论坛在上海同期举行。跨境贸易数字化领域的领导者劲升逻辑与安必快科技(深圳)有限公司(简称…

xilinx primitives(原语)

Xilinx的原语分为10类,包括:计算组件,IO端口组件,寄存器/锁存器,时钟组件,处理器组件,移位寄存器,配置和检测组件,RAM/ROM组件,Slice/CLB组件,G-t…

第一章:java类的继承

系列文章目录 文章目录 系列文章目录前言一、继承的基本概念二、继承的细节总结 前言 继承是类的重要特征之一。 一、继承的基本概念 ​​​​​​ 关键字extends,表示Sab类继承了Base类,则Sab为Base的子类,Base为Sab的父类。继承在现实中是…

苹果Mac电脑fcpx视频剪辑:Final Cut Pro中文最新 for mac

Final Cut Pro是苹果公司开发的一款专业视频剪辑软件,它为原生64位软件,基于Cocoa编写,支持多路多核心处理器,支持GPU加速,支持后台渲染。Final Cut Pro在Mac OS平台上运行,适用于进行后期制作。 Final Cu…

支持内录系统声音的Mac录屏软件Omi Recorder

Screen Recorder by Omi是一款功能强大的屏幕录制应用程序。它可用于在Windows和Mac计算机上捕获屏幕,以便进行演示、教程、游戏录制、视频编辑等各种用途。 以下是该应用程序的一些主要特点: 支持高清录制:Omi Screen Recorder可以以高达6…

嵌入式发展历史

MPU、MCU、SoC、Application Processors 在一个电子系统中,处理器占据最重要的位置,被称为中央处理器单元(CPU:Central Processing Unit)。它从IO设备读取数据,处理,然后输出。 CPU的发展历史…

数字博物馆如何设计搭建,一文了解数字博物馆解决方案

导言: 数字博物馆是一种创新性的文化机构,通过数字技术的应用,将传统博物馆的宝贵文化遗产以全新的方式呈现给观众。 一.数字博物馆是什么 博物馆是指利用数字技术和互联网等新媒体技术来展示和传播文物、艺术品等文化遗产的博物馆。数字物…

汽车软件单元测试的要点与意义

测试是一个非常基础的概念,这种基础让大家可以随意在它前面添加各种定语。 尽管这种添加的背后多数是不同的分类维度,但让测试本身成为了繁杂概念的集合,这也让我们总有种无法把握的烦躁感。 单元测试就是这堆让人烦躁的繁杂概念之一。 1、3种…

Linux文件系统目录结构

典型的Linux文件系统目录结构的列表 典型的Linux文件系统目录结构的列表。每个目录都有其特定的用途: /bin: 存放系统引导和修复所需的二进制可执行文件,如ls,cp,mv等命令。 /boot: 存放操作系统引导文件,例如内核和…

2023世界传感器大会开幕,汉威科技多领域创新产品引瞩目

11月5日,2023世界传感器大会在郑州国际会展中心正式拉开帷幕。据悉,本次大会由河南省人民政府、中国科学技术协会主办,郑州市人民政府、河南省工业和信息化厅、河南省科学技术协会、中国仪器仪表学会承办。 大会由“一会一赛一展”组成&#…

Python新手必读:容器类型使用的实用小贴士

更多资料获取 📚 个人网站:涛哥聊Python Python提供了多种容器类型,如列表(List)、元组(Tuple)、集合(Set)、字典(Dictionary)等,用于…

NVM安装与配置(管理node版本)

NVM安装与配置(管理node版本) 一、安装NVM 下载安装 NVM解压后点击exe文件进行安装:点击下一步安装到 D:\NVM 下先在D:\NVM 下创建nodejs文件夹,然后将路径设置如下:点击next 一直点击 完成安装;地方是非得失范德萨范德萨发![在…

如何建设企业集成中台

企业的信息化建设是伴随企业发展不断延伸、不断升级的过程,而随着信息化体量不断增大,复杂繁多的业务系统往往又成为信息化建设的瓶颈,而为了消除瓶颈,更便捷地打通系统的关联,针对企业实际业务建立集成中台则是非常有…

C语言基础篇2:数组

1 一维数组 数组是一个由若干同类型变量组成的集合,引用这些变量可以使用同一个名字。数组均由连续的存储单元组成,最低地址对应于数组的第一个元素,最高地址对应数组的最后一个元素。 1.1 一维数组的定义和引用 一维数组示意图如下&#xf…

海康威视解码器维修DS-6900系列DS-6916UD

海康威视解码器常见维修型号:DS-6916UD/DS-6901/DS-6904/DS-6908/DS-6910/DS-6912UD/6A16 DS-6A16UD 产品类型:视音频解码器纠错 I/O接口:输入 DVI-I纠错;输出 VGA,BNC纠错;音频输入 HDMI纠错 产品特性 …

CY5-COOH脂溶性羧基荧光染料1032678-07-1

Cyanine5-COOH是一种荧光染料,它CAS号1032678-07-1,分子式为C32H39ClN2O2,分子量为519.12。Cyanine5-COOH具有良好的光稳定性和荧光亮度,可以用于生物学研究、诊断、药物筛选等领域。 Cy5-COOH (来自星戈瑞的花菁染料) 含有羧基官…

【源码篇】基于SSM开发的社区论坛系统

系统介绍 基于SSM开发的社区论坛系统是一个分享创造的开发者社区,是高品质的开发者社区,致力于为开发者提供一个分享创造、结识伙伴、协同互助的平台。 系统功能说明 1、游客模式下可以查看系统发布的话题、及用户参与的讨论,可以查看系统…

在Linux中安装宝塔面板

在公网为x.x.x.x的服务器上安装宝塔面板 安装宝塔面板 第一步,下载安装宝塔面板。 命令:cd /usr/local/src wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh 注意:需要等几分钟来下载宝塔面…

【STM32】Systick定时器

一、STM32的5种定时器简介 1.独立看门狗(IWDG) VS 窗口看门狗(WWDG) 1.独立看门狗(IWDG) 独立看门狗:当没有到设定时间之前,给它喂了狗,就会回到初始值。 2.窗口看门狗…

JAVA对象大小的获取

1. Java 对象的内存布局 Java的实例对象、数组对象在内存中的组成包括如下三部分:对象头Hearder、实例数据、内存填充。示意图如下所示 对象头 其主要包括两部分数据:Mark Word、Class对象指针。特别地对于数组对象而言,其还包括了数组长度…