对状态模式的理解

news2025/4/18 1:28:14

对状态模式的理解

    • 一、场景
    • 二、不采用状态模式
      • 1、代码
      • 2、缺点
    • 三、采用状态模式
      • 1、代码
        • 1.1 状态类
        • 1.2 上下文(这里指:媒体播放器)
        • 1.3 客户端
      • 2、优点

一、场景

  • 同一个东西(例如:媒体播放器),有一些操作:暂停、播放、停止。很显然,每执行一种操作,媒体播放器的状态便改变了。

    • 播放 -> 暂停:

      • Media Player is now paused.

      当前是播放状态,点击暂停操作,输出:Media Player is not playing.

    • 暂停 -> 暂停

      • Media Player is not playing.

      当前是暂停状态,点击暂停操作,输出:Media Player is not playing.

    • 虽然是同一种操作,但是,因为媒体播放器处在不同的状态,所以表现不同。

  • 面对这种场景,我们怎么编码呢?

    • 简单粗暴的方式:写if-else或者switch。(详见:二、不采用状态模式)

    • 更好的方式:采用状态模式。(详见:三、采用状态模式)

      • 状态模式是一种行为设计模式, 让我们能在一个对象的内部状态变化时改变其行为。

二、不采用状态模式

1、代码

// 媒体播放器
public class MediaPlayer {
    private String currentState;

    public MediaPlayer() {
        this.currentState = "STOPPED"; // 初始状态为停止
    }

    public void play() {
        if (currentState.equals("STOPPED")) {
            System.out.println("Media Player is now playing.");
            currentState = "PLAYING";
        } else if (currentState.equals("PAUSED")) {
            System.out.println("Media Player resumed playing.");
            currentState = "PLAYING";
        } else {
            System.out.println("Media Player is already playing.");
        }
    }

    public void pause() {
        if (currentState.equals("PLAYING")) {
            System.out.println("Media Player is now paused.");
            currentState = "PAUSED";
        } else {
            System.out.println("Media Player is not playing.");
        }
    }

    public void stop() {
        System.out.println("Media Player is now stopped.");
        currentState = "STOPPED";
    }
}

// 客户端
public class Main {
    public static void main(String[] args) {
        MediaPlayer mediaPlayer = new MediaPlayer();
        System.out.println("Initial state: Stopped");

        mediaPlayer.play(); // Stopped -> Playing
        mediaPlayer.pause(); // Playing -> Paused
        mediaPlayer.play(); // Paused -> Playing
        mediaPlayer.stop(); // Playing -> Stopped
    }
}

/*
Initial state: Stopped
Media Player is now playing.
Media Player is now paused.
Media Player resumed playing.
Media Player is now stopped.
*/

2、缺点

  • 由于在设计之初,不一定能想到所有状态,假设一开始只想到了:play、pause、stop。辛辛苦苦写好了上面的代码。这时候,产品经理又提了新需求了,发现又要补2个状态。没办法,上面的play、pause、stop三个方法都要进行修改。改着改着就成屎山了。

三、采用状态模式

1、代码

1.1 状态类
  • 状态模式建议为对象的所有可能状态新建一个类, 每个状态独自实现自身状态下的各个行为。
// 抽象父类
public abstract class MediaPlayerState {
    protected MediaPlayer mediaPlayer;

    public MediaPlayerState(MediaPlayer mediaPlayer) {
        this.mediaPlayer = mediaPlayer;
    }

    protected abstract void play();
    protected abstract void pause();
    protected abstract void stop();
}

// 播放状态
public class MediaPlayerPlayState extends MediaPlayerState {
    public MediaPlayerPlayState(MediaPlayer mediaPlayer) {
        super(mediaPlayer);
    }

    @Override
    public void play() {
        System.out.println("Media Player is already playing.");
        mediaPlayer.setState(this);
    }

    @Override
    public void pause() {
        System.out.println("Media Player is now paused.");
        mediaPlayer.setState(new MediaPlayerPauseState(mediaPlayer));
    }

    @Override
    public void stop() {
        System.out.println("Media Player is now stopped.");
        mediaPlayer.setState(new MediaPlayerStopState(mediaPlayer));
    }
}

// 暂停状态
public class MediaPlayerPauseState extends MediaPlayerState {
    public MediaPlayerPauseState(MediaPlayer mediaPlayer) {
        super(mediaPlayer);
    }

    @Override
    public void play() {
        System.out.println("Media Player resumed playing.");
        mediaPlayer.setState(new MediaPlayerPlayState(mediaPlayer));
    }

    @Override
    public void pause() {
        System.out.println("Media Player is not playing.");
        mediaPlayer.setState(this);
    }

    @Override
    public void stop() {
        System.out.println("Media Player is now stopped.");
        mediaPlayer.setState(new MediaPlayerStopState(mediaPlayer));
    }
}

// 停止状态
public class MediaPlayerStopState extends MediaPlayerState {
    public MediaPlayerStopState(MediaPlayer mediaPlayer) {
        super(mediaPlayer);
    }

    @Override
    public void play() {
        System.out.println("Media Player is now playing.");
        mediaPlayer.setState(new MediaPlayerPlayState(mediaPlayer));
    }

    @Override
    public void pause() {
        System.out.println("Media Player is not playing.");
        mediaPlayer.setState(new MediaPlayerPauseState(mediaPlayer));
    }

    @Override
    public void stop() {
        System.out.println("Media Player is now stopped.");
        mediaPlayer.setState(this);
    }
}
1.2 上下文(这里指:媒体播放器)
public class MediaPlayer {
    private MediaPlayerState state;

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

    public MediaPlayer() {
        this.state = new MediaPlayerStopState(this);
    }

    public void play() {
        state.play();
    }

    public void pause() {
        state.pause();
    }

    public void stop() {
        state.stop();
    }
}
1.3 客户端
public class Main {
    public static void main(String[] args) {
        MediaPlayer mediaPlayer = new MediaPlayer();
        System.out.println("Initial state: Stopped");

        mediaPlayer.play(); // Stopped -> Playing
        mediaPlayer.pause(); // Playing -> Paused
        mediaPlayer.play(); // Paused -> Playing
        mediaPlayer.stop(); // Playing -> Stopped
    }
}

/*
Initial state: Stopped
Media Player is now playing.
Media Player is now paused.
Media Player resumed playing.
Media Player is now stopped.
*/

2、优点

  • 每个状态类,只需要关心自己状态下,每种操作应该执行哪些逻辑就好了。
  • 如果新增了状态,那无非就是新增一个类,其他已有的状态类,无非就是新写一个方法。这并不会去修改已有的代码,符合开闭原则。

上面的写法有一个小瑕疵,每个状态类实际上应该是单例模式(详见:对单例模式的饿汉式、懒汉式的思考),而不是每次切换状态的时候新建一个对象。

  • 不同状态下,有些操作是一样的,例如:停止操作。这时候可以采用组合的方式进行复用。将停止操作放到一个类中,例如:MediaPlayerStopHandler。每个方法调用MediaPlayerStopHandler的stop方法()实现停止。

  • 仔细看下状态模式的结构:​

    image

    • 会发现和策略模式的结构很像。但二者有很多不同:

      • (1)策略模式:

        • 1)根据客户端的输入,匹配一种策略。每个策略完全独立,策略A感知不到策略B。
        • 2)策略A和策略B是做同一件事情,仅仅是做法不同。
      • (2)状态模式:

        • 1)每种状态下,会有多种行为。(如果只有一种行为,也不存在状态转换了)
        • 2)状态A和状态B不是完全独立的,状态A执行某个行为后,会从状态A转移到状态B。

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

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

相关文章

vue2(webpack)集成electron和 electron 打包

前言 之前发过一篇vue集成electron的文章,但是用vue3vite实现的,在vue2webpack工程可能不适用,所以这篇文章就主要介绍vue2webpack集成electron方法 创建项目 vue create vue-electron-demo目录架构 vue-electron-demo/ ├── src/ …

C++内存管理优化实战:提升应用性能与效率

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle…

redis数据迁移之通过redis-dump镜像

这里写目录标题 一、redis-dump 镜像打包1.1 安装windows docker1.2 idea项目创建1.3 idea镜像打包 二、redis数据迁移2.1 数据导出2.2 数据导入 一、redis-dump 镜像打包 没有找到可用的redis-dump镜像,需要自己打包一下,这里我是在idea直接打包的 1.…

redis导入成功,缺不显示数据

SpringBootTest class SecurityApplicationTests {AutowiredStringRedisTemplate template; //添加这句代码,自动装载,即可解决文章三处代码报错Testvoid contextLoads() {String compact Jwts.builder().signWith(Jwts.SIG.HS512.key().build()).subj…

从表格到序列:Swift 如何优雅地解 LeetCode 251 展开二维向量

文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 在这篇文章中,我们将深入探讨 LeetCode 第 251 题——“展开二维向量”的问题。通过 Swift 语言,我们不仅会提供可运行的示例代码,还会结合实际场景进行…

小型园区网实验

划分VLAN SW3 [sw3]vlan batch 2 3 20 30 [sw3]interface GigabitEthernet 0/0/1 [sw3-GigabitEthernet0/0/1]port link-type access [sw3-GigabitEthernet0/0/1]port default vlan 2 [sw3-GigabitEthernet0/0/1]int g0/0/2 [sw3-GigabitEthernet0/0/2]port link-type acces…

c# 数据结构 链表篇 有关单链表的一切

本人能力有限,本文仅作学习交流与参考,如有不足还请斧正 目录 0.单链表好处 0.5.单链表分类 1.无虚拟头节点情况 图示: 代码: 头插/尾插 删除 搜索 遍历全部 测试代码: 全部代码 2.有尾指针情况 尾插 全部代码 3.有虚拟头节点情况 全部代码 4.循环单链表 几个…

VS Code连接服务器编写Python文件

1、下载 Visual Studio Code 2、打开扩展(ctrl shift x ) 3、搜索 Remote - SSH,安装 4、F1 或者 点金左下角 5、选择:Remote-SSH: Connect to Host……,回车 6、第一次用的时候,VS Code 会提示添加 SSH 主机。输…

Gitea的安装和配置以及应用

Gitea的安装和配置以及应用 一、安装 1、创建数据库和数据库账户(pg) su – postgres -c "psql" CREATE ROLE gitea WITH LOGIN PASSWORD gitea; CREATE DATABASE giteadb WITH OWNER gitea TEMPLATE template0 ENCODING UTF8 LC_COLLATE …

$_GET变量

$_GET 是一个超级全局变量,在 PHP 中用于收集通过 URL 查询字符串传递的参数。它是一个关联数组,包含了所有通过 HTTP GET 方法发送到当前脚本的变量。 预定义的 $_GET 变量用于收集来自 method"get" 的表单中的值。 从带有 GET 方法的表单发…

TBE(TVM的扩展)

算子 张量 一个张量只有一种数据类型 在内存中只能线性存储,最终形成一个长的一维数组 晟腾AI的数据格式 AIPP是对我们常见的数据格式转化成AI core支持的数据格式 广播机制 TVM TBE的第一种开发方式:DSL TBE的第二种开发方式:TVM TBE的第…

【Function Calling与Tool Calling】深度解析大模型智能中枢的架构革命

目录 一、范式转移:从对话引擎到智能中枢 二、核心技术解析 2.1 Function Calling技术栈 2.2 Tool Calling实现模式 三、企业级应用架构设计 3.1 智能工单系统案例 3.2 性能优化策略 四、安全与治理框架 4.1 权限控制矩阵 4.2 审计追踪设计 五、开发者实…

知识表示方法之六:过程表示法(Procedural Representation)

在人工智能的发展史中,关于知识的表示方法曾存在两种不同的观点。一种观点认为知识主要是陈述性的,其表示方法应着重将其静态特性,即事物的属性以及事物间的关系表示出来,称以这种观点表示知识的方法为陈述式或说明式表示法&#…

sql-labs靶场 less-2

文章目录 sqli-labs靶场less 2 联合注入 sqli-labs靶场 每道题都从以下模板讲解,并且每个步骤都有图片,清晰明了,便于复盘。 sql注入的基本步骤 注入点注入类型 字符型:判断闭合方式 (‘、"、’、“”&#xf…

git clone(复制)下载

1、复制 下载地址 2、打开网页,点击 克隆/下载按扭 3、按提示复制命令行到终端 4、VS里打开终端,并粘贴以下命令 5、 下载完毕 6、复制文件夹到你选定的位置 7、用VSCODE打开文件夹,开始你接下来的工作

Android设置adjustResize时无法生效 解决办法

删除Activity类下执行全屏的一行参数。 将图中这段Activity类中执行命令给删除就解决了。 注意关闭后状态栏和导航栏的透明度就无法自动处理了&#xff0c;需要到values和values-night下的themes.xml手动设置状态栏背景颜色。 <item name"android:statusBarColor"…

按键长按代码

这些代码都存放在定时器中断中。中断为100ms中断一次。 数据判断&#xff0c;看的懂就看吧

优选算法第八讲:链表

优选算法第八讲&#xff1a;链表 1.链表常用操作和技巧总结2.两数相加3.两两交换链表中的节点4.重排链表5.合并k个升序链表6.k个一组翻转链表 1.链表常用操作和技巧总结 2.两数相加 3.两两交换链表中的节点 4.重排链表 5.合并k个升序链表 6.k个一组翻转链表

4S店汽车维修保养管理系统 (源码+lw+部署文档+讲解),源码可白嫖!

摘要 二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式已经与当今4S店汽车维修保养管理系统的业务需求不相适应,也与4S店汽车维修保养管理系统化建设的发展趋势不相适应。本文针对这一需求设计并实现了…

【NLP 面经 8】

目录 一、文本生成任务 模型架构方面 训练数据方面 生成策略方面 二、命名实体识别任务NER 模型架构方面 特征工程方面 训练优化方面 三、情感分析任务 模型架构方面 训练数据方面 超参数调整方面 四、计算余弦相似度并添加符合条件结果 提示&#xff1a; 思路与算法 任由深渊的…