Java设计模式 十八 状态模式 (State Pattern)

news2025/1/24 23:15:21

状态模式 (State Pattern)

状态模式是一种行为型设计模式,它允许对象在其内部状态改变时改变其行为。状态模式让一个对象在其状态改变时,其行为也随之改变,看起来就像是改变了对象的类。通过将状态的变化封装到不同的状态对象中,状态模式可以使对象的行为更加灵活和可扩展。


1. 状态模式的组成

状态模式通常包含以下几个角色:

  • Context(上下文):上下文类维护一个当前状态的引用,它通常会委托给具体的状态类来执行相关行为。上下文类还可以动态地切换状态。
  • State(状态接口):定义了状态类的共同接口,所有具体的状态类都实现这个接口。
  • ConcreteState(具体状态类):每个具体的状态类实现了State接口,并定义了在该状态下的具体行为。

2. 状态模式的工作流程

  1. 上下文对象持有一个当前的状态对象,并通过调用状态对象的行为来执行相应的操作。
  2. 每个状态类(ConcreteState)实现了State接口,并提供了在该状态下的具体实现。
  3. 上下文可以在不同状态之间进行切换,通常由外部环境或条件触发。

3. 状态模式的实现

场景示例:电梯系统

我们来实现一个简单的电梯系统。电梯可以处于多个状态,比如:空闲运行停靠。每个状态下电梯的行为不同。通过状态模式,我们可以根据电梯的不同状态执行不同的行为。


1) 定义状态接口

状态接口定义了电梯所支持的操作,如openDoor()closeDoor()等。

// 状态接口
public interface ElevatorState {
    void openDoor();
    void closeDoor();
    void moveUp();
    void moveDown();
}

2) 定义具体状态类

每个具体的状态类实现了ElevatorState接口,并定义了该状态下电梯的具体行为。

// 空闲状态
public class IdleState implements ElevatorState {
    private Elevator elevator;

    public IdleState(Elevator elevator) {
        this.elevator = elevator;
    }

    @Override
    public void openDoor() {
        System.out.println("The door is opening.");
        elevator.setCurrentState(elevator.getOpenState());
    }

    @Override
    public void closeDoor() {
        System.out.println("The door is already closed.");
    }

    @Override
    public void moveUp() {
        System.out.println("The elevator is moving up.");
        elevator.setCurrentState(elevator.getMovingUpState());
    }

    @Override
    public void moveDown() {
        System.out.println("The elevator is moving down.");
        elevator.setCurrentState(elevator.getMovingDownState());
    }
}

// 开门状态
public class OpenState implements ElevatorState {
    private Elevator elevator;

    public OpenState(Elevator elevator) {
        this.elevator = elevator;
    }

    @Override
    public void openDoor() {
        System.out.println("The door is already open.");
    }

    @Override
    public void closeDoor() {
        System.out.println("Closing the door.");
        elevator.setCurrentState(elevator.getIdleState());
    }

    @Override
    public void moveUp() {
        System.out.println("Cannot move while the door is open.");
    }

    @Override
    public void moveDown() {
        System.out.println("Cannot move while the door is open.");
    }
}

// 上升状态
public class MovingUpState implements ElevatorState {
    private Elevator elevator;

    public MovingUpState(Elevator elevator) {
        this.elevator = elevator;
    }

    @Override
    public void openDoor() {
        System.out.println("Cannot open the door while the elevator is moving up.");
    }

    @Override
    public void closeDoor() {
        System.out.println("The door is already closed.");
    }

    @Override
    public void moveUp() {
        System.out.println("The elevator is already moving up.");
    }

    @Override
    public void moveDown() {
        System.out.println("Changing direction to move down.");
        elevator.setCurrentState(elevator.getMovingDownState());
    }
}

// 下降状态
public class MovingDownState implements ElevatorState {
    private Elevator elevator;

    public MovingDownState(Elevator elevator) {
        this.elevator = elevator;
    }

    @Override
    public void openDoor() {
        System.out.println("Cannot open the door while the elevator is moving down.");
    }

    @Override
    public void closeDoor() {
        System.out.println("The door is already closed.");
    }

    @Override
    public void moveUp() {
        System.out.println("Changing direction to move up.");
        elevator.setCurrentState(elevator.getMovingUpState());
    }

    @Override
    public void moveDown() {
        System.out.println("The elevator is already moving down.");
    }
}

3) 定义上下文类

Elevator类是上下文类,负责维护当前的状态,并委托给具体状态类执行操作。

// 电梯类(上下文)
public class Elevator {
    private ElevatorState idleState;
    private ElevatorState openState;
    private ElevatorState movingUpState;
    private ElevatorState movingDownState;

    private ElevatorState currentState;

    public Elevator() {
        idleState = new IdleState(this);
        openState = new OpenState(this);
        movingUpState = new MovingUpState(this);
        movingDownState = new MovingDownState(this);

        currentState = idleState;  // 默认初始状态为空闲状态
    }

    // 切换状态
    public void setCurrentState(ElevatorState state) {
        this.currentState = state;
    }

    // 获取当前状态
    public ElevatorState getCurrentState() {
        return currentState;
    }

    public ElevatorState getIdleState() {
        return idleState;
    }

    public ElevatorState getOpenState() {
        return openState;
    }

    public ElevatorState getMovingUpState() {
        return movingUpState;
    }

    public ElevatorState getMovingDownState() {
        return movingDownState;
    }

    // 电梯操作
    public void openDoor() {
        currentState.openDoor();
    }

    public void closeDoor() {
        currentState.closeDoor();
    }

    public void moveUp() {
        currentState.moveUp();
    }

    public void moveDown() {
        currentState.moveDown();
    }
}

4) 客户端代码

客户端代码模拟了电梯的操作,演示了不同状态下电梯的行为。

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

        // 电梯空闲时
        elevator.openDoor();  // 输出:The door is opening.
        elevator.moveUp();    // 输出:The elevator is moving up.
        
        // 电梯上升时
        elevator.moveDown();  // 输出:Changing direction to move down.
        elevator.openDoor();  // 输出:Cannot open the door while the elevator is moving down.
        
        // 电梯下降时
        elevator.closeDoor(); // 输出:The door is already closed.
        elevator.moveUp();    // 输出:Changing direction to move up.
    }
}

运行结果:
The door is opening.
The elevator is moving up.
Changing direction to move down.
Cannot open the door while the elevator is moving down.
The door is already closed.
Changing direction to move up.

4. 状态模式的优点

  1. 封装状态行为: 每个状态类封装了具体的状态行为,使得不同状态的行为不会混合在一起。
  2. 扩展性: 当需要添加新的状态时,可以通过增加新的状态类而不影响现有的代码,符合开闭原则。
  3. 清晰的状态转换: 状态的转换和每个状态的行为都清晰地定义在具体状态类中,使得代码更容易理解和维护。

5. 状态模式的缺点

  1. 类的数量增加: 每个状态都需要定义一个具体的状态类,当状态种类较多时,可能会导致类的数量激增,增加系统复杂度。
  2. 状态之间的相互依赖: 有时状态之间的转换逻辑较为复杂,可能会引发状态类之间的依赖关系,需要小心设计。

6. 状态模式的应用场景

  • 工作流引擎: 当某个任务根据不同的状态执行不同操作时,状态模式非常适用,例如审批流程中的不同状态(待审批、审批中、已通过、已拒绝等)。
  • 有限状态机: 适用于有限的状态集合,如游戏中的玩家状态(例如,待机、攻击、跳跃等)。
  • GUI组件: 例如,按钮、窗体等可以有不同的状态(激活、禁用、隐藏等),这些状态的行为可以通过状态模式来管理。

7. 总结

状态模式通过将每个状态的行为封装在独立的状态对象中,使得对象的行为随状态变化而变化。这种模式可以有效地管理和扩展具有多个状态的对象,特别是在复杂的状态转移和行为执行场景中,它提供了一种灵活且清晰的解决方案。

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

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

相关文章

vim如何显示行号

:set nu 显示行号 :set nonu 不显示行号 (vim如何使设置显示行号永久生效:vim如何使相关设置永久生效-CSDN博客)

国产编辑器EverEdit - 命令窗口应用详解

1 命令窗口应用详解 1.1 应用场景 有时需要在EverEdit中执行一些命令行工具,甚至想把当前文档做为参数,传递给命令进行一些文本分析,比如:一些常用的文本处理工具,gawk.exe等。 1.2 使用方法 命令窗口的使用在官方手…

Linux C\C++编程-文件位置指针与读写文件数据块

【图书推荐】《Linux C与C一线开发实践(第2版)》_linux c与c一线开发实践pdf-CSDN博客 《Linux C与C一线开发实践(第2版)(Linux技术丛书)》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 Linu…

vue2使用flv.js在浏览器打开flv格式视频

组件地址&#xff1a;GitHub - bilibili/flv.js: HTML5 FLV Player flv.js 仅支持 H.264 和 AAC/MP3 编码的 FLV 文件。如果视频文件使用了其他编码格式就打不开。 flv.vue <template><div><el-dialog :visible.sync"innerVisibleFlv" :close-on-pre…

Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到

Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到 这次在windows的WSL2中遇到了一个非常奇怪的错误&#xff0c;就是 CMake Error at /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:230 (message):Could NOT find BLAS (missing: BLAS_LIBRAR…

仿 RabbitMQ 的消息队列3(实战项目)

七. 消息存储设计 上一篇博客已经将消息统计文件的读写代码实现了&#xff0c;下一步我们将实现创建队列文件和目录。 实现创建队列文件和目录 初始化 0\t0 这样的初始值. //创建队列对应的文件和目录&#xff1a;public void createQueueFile(String queueName) throws IO…

多线程杂谈:惊群现象、CAS、安全的单例

引言 本文是一篇杂谈&#xff0c;帮助大家了解多线程可能会出现的面试题。 目录 引言 惊群现象 结合条件变量 CAS原子操作&#xff08;cmp & swap&#xff09; 线程控制&#xff1a;两个线程交替打印奇偶数 智能指针线程安全 单例模式线程安全 最简单的单例&…

腾讯 Hunyuan3D-2: 高分辨率3D 资产生成

腾讯 Hunyuan3D-2&#xff1a;高分辨率 3D 资产生成的突破 前言 在当今数字化时代&#xff0c;3D 资产生成技术正变得越来越重要。无论是游戏开发、影视制作还是虚拟现实领域&#xff0c;高质量的 3D 模型和纹理都是创造沉浸式体验的关键。然而&#xff0c;传统的 3D 资产制作…

R语言学习笔记之开发环境配置

一、概要 整个安装过程及遇到的问题记录 操作步骤备注&#xff08;包含遇到的问题&#xff09;1下载安装R语言2下载安装RStudio3离线安装pacman提示需要安装Rtools4安装Rtoolspacman、tidyfst均离线安装完成5加载tidyfst报错 提示需要安装依赖&#xff0c;试错逐步下载并安装…

DRG/DIP 2.0时代下基于PostgreSQL的成本管理实践与探索(上)

一、引言 1.1 研究背景与意义 在医疗领域的改革进程中&#xff0c; DRG/DIP 2.0 时代&#xff0c;医院成本管理的重要性愈发凸显。新的医保支付方式下&#xff0c;医院的收入不再单纯取决于医疗服务项目的数量&#xff0c;而是与病种的分组、费用标准以及成本控制紧密相关。这…

【数据结构】_顺序表

目录 1. 概念与结构 1.1 静态顺序表 1.2 动态顺序表 2. 动态顺序表实现 2.1 SeqList.h 2.2 SeqList.c 2.3 Test_SeqList.c 3. 顺序表性能分析 线性表是n个具有相同特性的数据元素的有限序列。 常见的线性表有&#xff1a;顺序表、链表、栈、队列、字符串等&#xff1b…

缓存之美:万文详解 Caffeine 实现原理(下)

上篇文章&#xff1a;缓存之美&#xff1a;万文详解 Caffeine 实现原理&#xff08;上&#xff09; getIfPresent 现在我们对 put 方法有了基本了解&#xff0c;现在我们继续深入 getIfPresent 方法&#xff1a; public class TestReadSourceCode {Testpublic void doRead() …

VSCode下EIDE插件开发STM32

VSCode下STM32开发环境搭建 本STM32教程使用vscode的EIDE插件的开发环境&#xff0c;完全免费&#xff0c;有管理代码文件的界面&#xff0c;不需要其它IDE。 视频教程见本人的 VSCodeEIDE开发STM32 安装EIDE插件 Embedded IDE 嵌入式IDE 这个插件可以帮我们管理代码文件&am…

HTTP 配置与应用(局域网)

想做一个自己学习的有关的csdn账号&#xff0c;努力奋斗......会更新我计算机网络实验课程的所有内容&#xff0c;还有其他的学习知识^_^&#xff0c;为自己巩固一下所学知识&#xff0c;下次更新HTTP 配置与应用&#xff08;不同网段&#xff09;。 我是一个萌新小白&#xf…

LiteFlow Spring boot使用方式

文章目录 概述LiteFlow框架的优势规则调用逻辑规则组件定义组件内数据获取通过 DefaultContext自定义上下文 通过 组件规则定义数据通过预先传入数据 liteflow 使用 概述 在每个公司的系统中&#xff0c;总有一些拥有复杂业务逻辑的系统&#xff0c;这些系统承载着核心业务逻…

mysql学习笔记-数据库的设计规范

1、范式简介 在关系型数据库中&#xff0c;关于数据表设计的基本原则、规则就称为范式。 1.1键和相关属性的概念 超键:能唯一标识元组的属性集叫做超键。 候选键:如果超键不包括多余的属性&#xff0c;那么这个超键就是候选键 主键:用户可以从候选键中选择一个作为主键。 外…

计算机网络 (55)流失存储音频/视频

一、定义与特点 定义&#xff1a;流式存储音频/视频是指经过压缩并存储在服务器上的多媒体文件&#xff0c;客户端可以通过互联网边下载边播放这些文件&#xff0c;也称为音频/视频点播。 特点&#xff1a; 边下载边播放&#xff1a;用户无需等待整个文件下载完成即可开始播放…

60,【1】BUUCF web [RCTF2015]EasySQL1

先查看源码 1&#xff0c;changepwd&#xff08;修改密码&#xff09; <?php // 开启会话&#xff0c;以便使用会话变量 session_start();// 设置页面的内容类型为 HTML 并使用 UTF-8 编码 header("Content-Type: text/html; charsetUTF-8");// 引入配置文件&…

我谈概率论与数理统计的知识体系

学习概率统计二十多年后&#xff0c;在廖老师的指导下&#xff0c;厘清了各章之间的关系。本来就是一条线两个分支&#xff0c;脉络很清晰。 分支一&#xff1a;从随机现象到样本空间到随机事件再到概率。 从随机事件到随机变量&#xff1a;为了进行定量的数学处理&#xff0…

基于海思soc的智能产品开发(视频的后续开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们讨论了camera&#xff0c;也讨论了屏幕驱动&#xff0c;这些都是基础的部分。关键是&#xff0c;我们拿到了这些视频数据之后&#xff0c;…