Java设计模式-状态模式

2023-11-11

状态模式

  在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。如人的情绪有高兴的时候和伤心的时候,不同的情绪有不同的行为,当然外界也会影响其情绪变化。

  对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用 if-else 语句来做状态判断,再进行不同情况的处理。但当对象的状态很多时,程序会变得很复杂。而且增加新的状态要添加新的 if-else 语句,这违背了“开闭原则”,不利于程序的扩展。

  以上问题如果采用“状态模式”就能很好地得到解决。状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,放到一系列的状态类当中,这样可以把原来复杂的逻辑判断简单化。

状态模式的定义与特点

  状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
  状态模式是一种对象行为型模式,其主要优点如下。

  • 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  • 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  • 有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

  状态模式的主要缺点如下。

  • 状态模式的使用必然会增加系统的类与对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

状态模式的结构与实现

  状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。

1. 状态模式的结构

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

  • 环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为。

状态模式的结构图如图所示

状态模式的结构图

2. 状态模式的实现

  状态模式的实现代码如下:

//StateTest.java
package State;

public class StateTest {
    public static void main(String[] args) {
        Context context = new Context();    //创建环境
        context.Handle();    //处理请求
        context.Handle();
        context.Handle();
        context.Handle();
    }
}

//环境类
class Context {
    private State state;

    //定义环境类的初始状态
    public Context() {
        this.state = new ConcreteStateA();
    }

    //设置新状态
    public void setState(State state) {
        this.state = state;
    }

    //读取状态
    public State getState() {
        return (state);
    }

    //对请求做处理
    public void Handle() {
        state.Handle(this);
    }
}

//抽象状态类
abstract class State {
    public abstract void Handle(Context context);
}

//具体状态A类
class ConcreteStateA extends State {
    public void Handle(Context context) {
        System.out.println("当前状态是 A.");
        context.setState(new ConcreteStateB());
    }
}

//具体状态B类
class ConcreteStateB extends State {
    public void Handle(Context context) {
        System.out.println("当前状态是 B.");
        context.setState(new ConcreteStateA());
    }
}

程序运行结果如下:

当前状态是 A.
当前状态是 B.
当前状态是 A.
当前状态是 B.

状态模式应用场景

  通常在以下情况下可以考虑使用状态模式。

  • 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

状态模式的扩展

  在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享,其结构图如下图所示。
共享状态模式的结构图

代码实现如下:

//FlyweightStateTest.java
package State;

import java.util.HashMap;

public class FlyweightStateTest {
    public static void main(String[] args) {
        ShareContext context = new ShareContext(); //创建环境
        context.Handle(); //处理请求
        context.Handle();
        context.Handle();
        context.Handle();
    }
}

//环境类
class ShareContext {
    private ShareState state;
    private HashMap<String, ShareState> stateSet = new HashMap<String, ShareState>();

    public ShareContext() {
        state = new ConcreteState1();
        stateSet.put("1", state);
        state = new ConcreteState2();
        stateSet.put("2", state);
        state = getState("1");
    }

    //设置新状态
    public void setState(ShareState state) {
        this.state = state;
    }

    //读取状态
    public ShareState getState(String key) {
        ShareState s = (ShareState) stateSet.get(key);
        return s;
    }

    //对请求做处理
    public void Handle() {
        state.Handle(this);
    }
}

//抽象状态类
abstract class ShareState {
    public abstract void Handle(ShareContext context);
}

//具体状态1类
class ConcreteState1 extends ShareState {
    public void Handle(ShareContext context) {
        System.out.println("当前状态是: 状态1");
        context.setState(context.getState("2"));
    }
}

//具体状态2类
class ConcreteState2 extends ShareState {
    public void Handle(ShareContext context) {
        System.out.println("当前状态是: 状态2");
        context.setState(context.getState("1"));
    }
}

程序运行结果如下:

当前状态是: 状态1
当前状态是: 状态2
当前状态是: 状态1
当前状态是: 状态2
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java设计模式-状态模式 的相关文章

  • 将 WAR 部署到 Tomcat(Spring Boot + Angular)

    我正在尝试使用以下命令部署 Spring Boot 应用程序WAR包装至Tomcat 10 应用程序已成功部署 但是 当我尝试访问端点时 它会导致404 未找到 战争文件 应用程序 war http localhost 8080 appli
  • 在java代码中创建postgresql表

    我有一个与 postgreSQL 数据库连接的 java 代码 现在 我希望当它连接到数据库时 我还将创建数据库表 但我的问题是 它不会创建数据库 我不知道问题是什么 这是我的代码 Statement st null ResultSet r
  • JAX-WS 入门 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有人可以推荐一些关于 JAX WS 入门的好教程吗 使用各种工具 如 wsgen 等 您可以从这里开始 通过 Java SE 6 平台介绍
  • Jenkins 未显示 Maven 编译器错误

    在 Jenkins 中构建多模块 maven 3 项目时 如果出现构建错误 我们会收到一条神秘消息 表明 Maven 编译器插件失败 这在上周才刚刚开始发生 INFO BUILD FAILURE INFO INFO Total time 1
  • Android 信号 11 (SIGSEGV),代码 1 (SEGV_MAPERR) libwebviewchromium.so

    对于 android 4 4 我多次收到 Native crash at system lib libwebviewchromium so 错误 以下是设备包括 Xperia Z1 SO 01F 16 30 2 Galaxy Tab4 7
  • 传递自定义类型查询参数

    如何接受自定义类型查询参数 public String detail QueryParam request final MYRequest request 上面的行在启动服务器时出现错误 jersey server model ModelV
  • 用于制作代码编辑器的 JavaFX 相当于 JSyntaxPane 的什么?

    以前在 Swing 中 我使用过JSyntaxPane用于制作一个小型 Java 源代码编辑器 为了练习 我决定用 JavaFX 重做整个项目并添加对更多语言的支持 最好是尽可能多 不过好像没有什么类似的JSyntaxPane 一些研究让我
  • Google 表格使用 API 密钥而不是 client_secret.json

    In the QuickStart java示例Java 快速入门 https developers google com sheets api quickstart java他们使用OAuth client ID识别该应用程序 这会弹出一
  • 使用正则表达式验证电子邮件的最大长度

    我找到了用于电子邮件验证的正则表达式 a z0 9 a z0 9 a z0 9 a z0 9 a z 2 4 我希望电子邮件的最大长度为 20 个字符 因此我将其更改为 a z0 9 a z0 9 a z0 9 a z0 9 a z 2 4
  • 是否可以使用 Apache Tika 提取表信息?

    我正在寻找 pdf 和 MS Office 文档格式的解析器 以从文件中提取表格信息 当我看到 Apache Tika 时 正在考虑编写单独的实现 我能够从任何这些文件格式中提取全文 但我的要求是提取表格数据 我希望有 2 列采用键值格式
  • 递归取消 allOf CompletableFuture

    如果我有 CompletableFuture
  • 生成一定长度的所有排列

    假设我们有一个字母表 abcdefghiklimnop 如何以有效的方式以五个一组的形式重复该字母表来递归生成排列 几天来我一直在为此苦苦挣扎 任何反馈都会有帮助 本质上这与 生成给定字符串的所有排列 https stackoverflow
  • java.exe 以非零退出值 1 结束

    只是为了开始 我并不是真正尝试从 Android 中的 xlsx 文件中读取单元格 我已经尝试了几乎所有我在 Google 上搜索到的内容 但是每次 在两台不同的 PC 上 都是 Java 1 7 0 79 当我尝试构建 运行 这个应用程序
  • java绕中心旋转矩形

    我想围绕其中心点旋转一个矩形 它应该保留在应该绘制的位置并在该空间中旋转 这是我的代码 AffineTransform transform new AffineTransform transform rotate Math toRadian
  • 为 REST API 生成 Swagger UI 文档

    我使用 Java 中的 JAX RS Jersey 开发了 REST API 我想为其转换 生成基于 Swagger 的 UI 文档 谁能以简单的方式告诉我如何做到这一点的精确 步骤 很抱歉 他们网站上给出的步骤对我来说有点模糊 有多种方法
  • Spring Security 角色层次结构不适用于 Thymeleaf sec:authorize

    我正在使用 Spring Security 3 2 5 RELEASE 和 ThymeLeaf 2 1 4 RELEASE 我已经在安全上下文中定义了角色层次结构 在我的视图层中我正在使用sec authorize属性来定义菜单项 我希望看
  • 获取接收者的设备令牌以在 Firebase 中发送通知

    所以我正在学习如何使用 firebase 发送设备到设备的通知 我看到了这个answer https stackoverflow com a 42548586 5237289发送通知 看起来很简单 现在 我知道要获取发件人的令牌 它应该如下
  • 在 Kotlin 中声明静态属性?

    My Java code public class Common public static ModelPengguna currentModelPengguna public class Common companion object v
  • JPA ManyToMany 产生的空联接表

    我有一个应用程序 其中我尝试使用 Hibernate 作为 JPA 提供程序来实现两个实体之间的多对多关系 我正在尝试的例子是一个单向的 其中一个相机可以有多个镜头 而镜头可以安装到多个相机中 以下是我的实体类 只需粘贴其中的相关部分 Ca
  • Graphics2D setfont() 严重减慢了 java 应用程序的启动速度

    我正在用java制作一个游戏 它每秒刷新60次 每次执行循环时 我都会使用 g2d 来绘制图像和字符串 如果我这样做的话一切都会很好g2d setFont new Font Arial Font PLAIN 8 和抽绳 这将是正常的 但如果

随机推荐

  • 云盾身份认证二要素接口提示参数非法(curl用get方法传中文参数乱码)

    接口文档给的示例代码如下图
  • php邮箱发送文件详解

    双击打开 双击打开 双击打开 邮箱文件链接 https pan baidu com s 1EPLecVwICxwTEqUo3p ktg 提取码 pja6
  • vue 数组对象转数组

    let arr let obj a 1 a 2 for let i in obj arr push obj i a 打印 1 2
  • Github上传大文件

    1 访问github 这里挂了梯子还进不去 可以ping通github但无法访问网页 需要修改hosts 添加以下内容 参考 https blog csdn net suzhiwei boke article details 1251643
  • 《自然语言处理实战 01》商品信息与文本数据的挖掘分析

    文章目录 书山有路勤为径 学海无涯苦作舟 一 数据的导入 1 1 模块设置 1 2 数据导入 1 3 数据的log变换 二 商品的包邮 类别 价格 品牌分析 2 1 包邮分析 2 2 类别分析 2 2 1 类别细分 2 2 2各类别分析 2
  • 可以用声明变量的方式创建链表吗

    绝了 今天异想天开 不用malloc动态申请内存 而用局部变量声明的方法去创建链表 让我这个刚学习链表的人搞懂了为什么要动态申请内存 左图 代码 include
  • Python实现中文字幕雨+源代码

    前言 最近浏览了很多关于用Python和Pygame实现代码雨的案例 发现很多都是没有深入讲解代码的整个实现过程 从0到1教会你制 作中文文字雨 然后在介绍的过程中 我也将深入介绍Pygame的基础知识 让你从一个小白 学完之后也能对Pyg
  • 目标人脸检测与识别(计算机视觉)

    一 实验目的 通过python 语言编程设计人脸检测算法 以此人脸作为训练样本 训练目标人脸模型 进一步实现目标人脸的识别 通过上述编程促进学生理解并掌握人脸检测及识别的相关原理 同时培养学生的编程能力 二 实验硬 软件环境 笔记本电脑 w
  • hex码与float在线相互转换链接

    hex转gloat链接 float转hex链接
  • centOS yacc lex

    yacc command not found configure error Your operating system s lex is insufficient to compile libpcap flex is a lex repl
  • 指针数组(例题详解)

    include
  • js代码让iframe窗口全屏

    最近在工作中使用novnc远程连接电脑 在ifrmae中嵌入novnc页面 点击全屏按钮 能够让iframe页面全屏 我的思路是 1 点击全屏按钮 让当前页面全屏 2 将iframe重新fixed定位 高宽设置100 left top为0
  • jenkins介绍部署及三种构建方式配置

    1 前言 1 1 jenkins介绍 jenkins是基于java开发的一种持续集成工具 用于监控持续重复的工作 功能包括 1 持续的软件版本发布 测试 2 监控外部调用执行项目 Jenkins其实很早之前就有了 最近火起来的原因是 大家都
  • secureCRT 查看日志常用命令

    cd 进入根目录 cd 回到自己的目录 用户不同则目录也不同 root为 root xxt为 home xxt cd 回到上级目录 pwd 显示当前所在的目录 ls 显示当前目录下的所有文件 grep catalina out 在日志中查找
  • 【1G-6G】移动通信技术发展

    移动通信技术发展 1G 早在1947年 贝尔实验室的科学家就提出了蜂窝通信的概念 在20世纪60年代对此进行了系统的实验 20世纪60年代末 70年代初开始出现了第一个蜂窝 Cellular 系统 蜂窝的意思是将一个大区域划分为若干个相邻的
  • ATT&CK - T1546.003

    事件触发的执行 WMI事件订阅 目的 出现场景 ATT CK T1546 003 https attack mitre org techniques T1546 003 检查方式 复现方式 目的 建立持久性 出现场景 比如一些病毒的启动方式
  • excel 两列模糊匹配给出结果_EXCEL快速对比两列数据的不同

    作者 Miss 蜗牛 链接 https www jianshu com p 68b867d4558a 在工作中 我们经常需要对比两列数据或文本是否相同 如果是比较简单并且比较少的时候 我们可以肉眼一个一个的核对 或者都是数字的时候 可以用减
  • MySQL数据库和Oracle数据库的区别

    由于SQL Server不常用 所以这里只针对MySQL数据库和Oracle数据库的区别 1 对事务的提交 MySQL默认是自动提交 而Oracle默认不自动提交 需要用户手动提交 需要在写commit 指令或者点击commit按钮 2 分
  • TensorFlow Lite 入门样例,亲测有效

    参考链接 tensorflow 物体检测模型相关资料 https github com tensorflow models tree master research object detection java api接口 https ten
  • Java设计模式-状态模式

    状态模式 在软件开发过程中 应用程序中的有些对象可能会根据不同的情况做出不同的行为 我们把这种对象称为有状态的对象 而把影响对象行为的一个或多个动态变化的属性称为状态 当有状态的对象与外部事件产生互动时 其内部状态会发生改变 从而使得其行为