行为型模式--状态模式

news2024/11/16 19:38:35

目录

举例

状态模式

定义

结构

 代码实现

优缺点

优点:

缺点:

使用场景

举例

【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一 种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进 行开门操作,而如果电梯门是停止状态,就可以执行开门操作。

类图如下:

问题分析:

比如其中的open方法:

//执行开门动作
@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;
        }
}

使用了大量的switch…case这样的判断(if…else也是一样),使程序的可阅读性变差。 扩展性很差。如果新加了断电的状态,我们需要修改上面判断逻辑。这个时候就需要用到状态模式

状态模式

定义

对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变 时改变其行为。

结构

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

环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并 将与状态相关的操作委托给当前状态对象来处理。

抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。

具体状态(Concrete State)角色:实现抽象状态所对应的行为。

 代码实现

抽象状态类

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

具体状态类:

开启状态:

//开启状态
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();
    }
}

环境角色:

public class Context {
    //定义出所有的电梯状态
    //开门状态,这时候电梯只能关闭
    public final static OpenningState openningState = newOpenningState();
    //关闭状态,这时候电梯可以运行、停止和开门
    public final static ClosingState closeingState = new ClosingState();
    //运行状态,这时候电梯只能停止
    public final static RunningState runningState = new RunningState();
    //停止状态,这时候电梯可以开门、运行
    public final static StoppingState stoppingState = newStoppingState();
    //定义一个当前电梯状态
    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();
    }
}

测试类:

//测试类
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();
    }
}

优缺点

优点:

将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态 即可改变对象的行为。

允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块(不面向对象)。

缺点:

状态模式的使用必然会增加系统类和对象的个数。

状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。

状态模式对"开闭原则"的支持并不太好。

使用场景

当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使 用状态模式。

一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

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

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

相关文章

Xdebug的安装及使用

Xdebug的安装及使用 前言一、Xdebug如何配置二、PHPstrom配置三、Xdebug的使用1.面板功能解释2.调试功能详解 四、Xdebug原理 前言 软件调试是泛指重现软件缺陷问题,定位和 查找问题根源,最终解决问题的过程,编写的程序不可能一直不出错,所以调试很重要调试通常有如…

西安阿里云代理商:阿里云服务器的可扩展性和弹性如何?是否支持按需付费?

西安阿里云代理商:阿里云服务器的可扩展性和弹性如何?是否支持按需付费?   一、阿里云服务器的可扩展性   阿里云作为业界知名的云服务提供商,其服务器具有极强的可扩展性。可扩展性主要体现在以下几方面:   1. …

小米note3刷机-从miui12刷回miui9

小米note3刷机-从miui12刷回miui9 文章目录 小米note3刷机-从miui12刷回miui9解除BL锁进入开发者模式遇到的问题解决BootLoader无法连接电脑的问题 导包 3月份原本想买一部小米6回来刷机,结果发现小米6的二手价格有一点点high。然后就选了一个 大平版 小米note3 但是直到昨天…

SpringBoot 如何使用 Logback 进行日志记录

SpringBoot 如何使用 Logback 进行日志记录 在开发 Web 应用程序时,日志记录是非常重要的一部分。日志可以帮助我们跟踪应用程序的运行情况,并帮助我们快速地排查问题。在 SpringBoot 中,我们可以使用 Logback 进行日志记录。Logback 是一款…

F407/103MAP文件

认识MAP文件 MDK编译工程,会生成一些中间文件(如.o、.axf、.map 等),最终生成hex文件,以便下载到MCU上面执行。这些文件分为 11 个类型,其中4种文件比较重要。 比如: 本文主要讲解map文件。 map…

第四章 死锁

目录 一、死锁的概念 1.1 什么是死锁 1.2 死锁、饥饿、死循环的区别 1.2.1 死锁 1.2.2 饥饿 1.2.3 死循环 1.2.4 三者间的异同 1.3 死锁产生的必要条件 1.3.1 互斥条件 1.3.2 不剥夺条件 1.3.3 请求和保持条件 1.3.4 循环等待条件 1.4 什么时候会发生死锁 1.5 …

深入理解Java中的synchronized

文章目录 前言正文一、多线程操作同一数据时的问题二、问题分析三、synchronized 解决问题四、synchronized 是怎么解决问题的五、Java1.6时的优化5.1 自旋锁5.2 自适应锁5.3 锁消除5.4 锁粗化5.5 偏向锁(单线程高效场景)5.2 轻量级锁(多线程…

MySQL数据表查询

😇作者介绍:一个有梦想、有理想、有目标的,且渴望能够学有所成的追梦人。 🎆学习格言:不读书的人,思想就会停止。——狄德罗 ⛪️个人主页:进入博主主页 🗼专栏系列:进入MySQL知识专…

IO、存储、文件系统的简单介绍

目录 一.什么是IO 第一类:存储器IO 第二类:设备IO 二.存储 三:文件系统 总结 一.什么是IO I(input):放入数据 O(output):取出数据 所以我们平时说的IO,实际上就是放入数据和存储数据的意思 在这里,我们一般将IO又分为两大类 第一类:存储器IO 这类IO主要针对的是计算机中…

2023年最新同步网盘排行榜,了解哪些平台适合您的文件同步需求!

在数码领域,同步盘是一个极其受欢迎的工具,它可以帮助人们在不同设备之间共享文件。作为同步盘用户,我们关心的一个很重要的问题就是,在同步盘市场上,哪些同步盘是最好的? 今天我们综合了不同的产品测评网站…

Vulnhub: Corrosion:2靶机

kali:192.168.111.111 靶机:192.168.111.131 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.131 通过nmap脚本枚举出8080端口存在backup.zip文件,下载后解压发现需要密码,利用john爆破压缩包密…

【SSM项目整合流程】

目录 一.用Maven创建一个project项目 1.1新建一个项目,选择Maven然后点击下一步 1.2设置项目名称和AGV后点击完成 1.3在pom.xml文件中导入依赖和配置打包方式 二.添加web工程 2.1在Project Structure中型添加一个web工程 2.2配置web.xml 三.创建SpringMVC的…

2.设置Salesforce开发环境

文章目录 前言1. 关于Salesforce DX环境2. 配置Visual Studio Code2.1 安装CLI2.2 设置Visual Studio Code 3. 创建一个Hello World Lightning web component 来检证开发环境3.1 创建一个Salesforce DX project3.2 将deploy好的组件加到lightning App中 前言 此处解释关于本文…

数据结构——C语言实现常见排序(插入排序、希尔排序、选择排序、堆排序、冒泡排序)

引言: 现在是北京时间2023年6月23日13点19分,度过了一个非常愉快的端午节。由于刚从学校回家,一下子伙食强度直升了个两三个档次。这也导致我的肠胃不堪重负,我也准备等会去健身房消耗一下盈余的热量。回到家陪伴爷爷走人生最后的…

C++11 线程库—线程操作(更新中)

前言 在C11推出线程库前,Windows和Linux操作系统的线程操作并不同,这就导致多线程程序无法跨平台,如果想要跨平台,会很麻烦并且容易出错。C11推出的线程库就解决了这一问题。 因为在Windows和Linux操作系统中有一些独特的常量&am…

OpenGL 鼠标拾取模型

1.简介 在我们的场景中,使用鼠标光标点击或“挑选”一个3d对象是很有用的。一种方法是从鼠标投射3d光线,通过相机,进入场景,然后检查光线是否与任何物体相交。这通常被称为光线投射。 我们不是从局部空间中的网格开始&#xff0c…

gRPC 实践

RPC 包管理,1.12前;旧版本要设置GO111MODULEoff;查找gopath/src;goroot/src;几乎没有包管理; 新版本;go.mod; module xxx go version设置GO111MODULEon 是什么 远程过程调用; …

Windows页面置换算法与文件操作

实验一 一、实验内容或题目: 随机产生页面访问序列,并实现LRU, FIFO, OPT三种算法进行缺页比较 二、实验目的与要求: 1、编写程序,随机产生页面访问序列,并实现LRU, FIFO, OPT三种算法进行缺页比较。 2、理解三种算…

自监督学习简介

1.  自监督学习 自监督学习是可以看做是一种特殊的无监督学习的一个子类别(但并非无监督学习),因为它利用了未标记的数据。 关键思想是让模型无需手动标签即可学习数据表示。一旦模型学会了如何表示数据,那么它就可以用较少量的…

liunx+docker+rabbitmq安装延迟队列插件

安装版本 rabbit: RabbitMQ 3.8.16 erlang: Erlang 23.3.2 rabbit: rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez 准备 1.rabbmitMQ 安装 docker pull rabbitmq 2.rabbmitMQ 启动 docker run -d --hostname my-rabbit --name rabbit -e RABBITMQ_DEFAULT_USER用户…