状态模式原理剖析
《状态模式原理剖析》
状态模式(State Pattern) 是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。换句话说,当对象状态发生变化时,它的行为也会随之变化。
通过状态模式,可以消除通过 if-else 或 switch-case 来判断状态的需要。每个状态的行为封装在独立的类中
核心思想:
状态模式将对象的不同状态封装成独立的类,并让对象在不同的状态下有不同的行为。状态模式通过将状态的行为和逻辑封装在状态类中,使得状态之间的转换变得清晰、易扩展。
UML 类图:状态模式

角色说明:
- Context(上下文类):
- 持有一个
State对象,表示当前的状态。 - 负责将状态的转换委托给具体的状态类。
- 持有一个
- State(抽象状态类):
- 定义了一个
handle()方法,用于处理当前状态的逻辑。
- 定义了一个
- ConcreteState(具体状态类):
- 实现
State接口,负责在具体状态下的行为。 - 不同的具体状态类表示对象在不同状态下的不同行为。
- 实现
案例:订单状态管理
场景描述:
在电商平台或者订餐系统中,订单的状态是一个典型的使用状态模式的场景。订单的状态通常包括以下几种:
- 新订单(
NewOrder):订单刚创建。 - 已付款(
Paid):订单已付款,等待发货。 - 已发货(
Shipped):订单已经发货,等待确认收货。 - 已完成(
Completed):订单交易完成。 - 取消订单(
Cancelled):订单被取消。
每个订单的状态都会影响订单的行为。例如,只有在新订单状态下,用户才可以取消订单;在已付款状态下,用户不能取消订单,但可以查询发货状态;而在已完成或取消状态下,订单是不可修改的。
状态模式处理的好处:
- 避免复杂的
if-else条件判断:不同状态下的订单行为各不相同,使用状态模式可以避免在代码中出现大量的if-else条件判断(如:if(order.status == "paid") { ... } else if(order.status == "shipped") { ... })。 - 状态行为封装:将每种状态的行为封装到相应的状态类中,使得状态切换清晰,便于维护和扩展。
- 提高扩展性:当需要新增或修改订单状态时,可以通过新增状态类而不影响现有代码逻辑,符合开闭原则。
代码实现:订单状态管理
Step 1: 定义状态接口
我们首先定义一个 OrderState 接口,声明了订单状态下的所有可能的行为,比如支付、发货、取消和完成
// 状态接口:订单状态
public interface OrderState {void pay(OrderContext context);void ship(OrderContext context);void cancel(OrderContext context);void complete(OrderContext context);
}
Step 2: 实现具体的状态类
新订单状态(NewOrderState)
当订单处于新订单状态时,可以进行支付或取消操作,但不能发货或完成。
// 具体状态类:新订单状态
public class NewOrderState implements OrderState {@Overridepublic void pay(OrderContext context) {System.out.println("Order paid. Moving to Paid state.");context.setState(new PaidOrderState());}@Overridepublic void ship(OrderContext context) {System.out.println("Cannot ship order. Order is not paid yet.");}@Overridepublic void cancel(OrderContext context) {System.out.println("Order cancelled.");context.setState(new CancelledOrderState());}@Overridepublic void complete(OrderContext context) {System.out.println("Cannot complete order. Order is not paid yet.");}
}
已付款状态(PaidOrderState)
当订单处于已付款状态时,可以发货,但不能取消订单。
// 具体状态类:已付款状态
public class PaidOrderState implements OrderState {@Overridepublic void pay(OrderContext context) {System.out.println("Order is already paid.");}@Overridepublic void ship(OrderContext context) {System.out.println("Order shipped. Moving to Shipped state.");context.setState(new ShippedOrderState());}@Overridepublic void cancel(OrderContext context) {System.out.println("Cannot cancel. Order is already paid.");}@Overridepublic void complete(OrderContext context) {System.out.println("Cannot complete order. Order is not shipped yet.");}
}
已发货状态(ShippedOrderState)
当订单处于已发货状态时,可以完成订单,但不能再发货或取消订单。
// 具体状态类:已发货状态
public class ShippedOrderState implements OrderState {@Overridepublic void pay(OrderContext context) {System.out.println("Order is already paid and shipped.");}@Overridepublic void ship(OrderContext context) {System.out.println("Order is already shipped.");}@Overridepublic void cancel(OrderContext context) {System.out.println("Cannot cancel. Order is already shipped.");}@Overridepublic void complete(OrderContext context) {System.out.println("Order completed. Moving to Completed state.");context.setState(new CompletedOrderState());}
}
已完成状态(CompletedOrderState)
订单已经完成后,所有操作都无法再进行。
// 具体状态类:已完成状态
public class CompletedOrderState implements OrderState {@Overridepublic void pay(OrderContext context) {System.out.println("Cannot pay. Order is already completed.");}@Overridepublic void ship(OrderContext context) {System.out.println("Cannot ship. Order is already completed.");}@Overridepublic void cancel(OrderContext context) {System.out.println("Cannot cancel. Order is already completed.");}@Overridepublic void complete(OrderContext context) {System.out.println("Order is already completed.");}
}
取消订单状态(CancelledOrderState)
订单被取消后,所有操作都无法再进行。
// 具体状态类:取消订单状态
public class CancelledOrderState implements OrderState {@Overridepublic void pay(OrderContext context) {System.out.println("Cannot pay. Order is cancelled.");}@Overridepublic void ship(OrderContext context) {System.out.println("Cannot ship. Order is cancelled.");}@Overridepublic void cancel(OrderContext context) {System.out.println("Order is already cancelled.");}@Overridepublic void complete(OrderContext context) {System.out.println("Cannot complete. Order is cancelled.");}
}
Step 3: 创建上下文类
OrderContext 持有订单的当前状态,并且通过调用当前状态的行为方法来执行操作。
// 上下文类:订单上下文
public class OrderContext {private OrderState currentState;public OrderContext() {this.currentState = new NewOrderState(); // 初始状态为新订单}public void setState(OrderState state) {this.currentState = state;}public void pay() {currentState.pay(this);}public void ship() {currentState.ship(this);}public void cancel() {currentState.cancel(this);}public void complete() {currentState.complete(this);}
}
Step 4: 测试状态模式
public class OrderStatePatternDemo {public static void main(String[] args) {OrderContext order = new OrderContext();// 订单状态:新订单order.pay(); // 支付订单order.ship(); // 发货订单order.complete(); // 完成订单// 尝试取消已完成订单order.cancel(); // 无法取消已完成订单}
}
输出结果:
Order paid. Moving to Paid state.
Order shipped. Moving to Shipped state.
Order completed. Moving to Completed state.
Cannot cancel. Order is already completed.
状态模式解决的问题
- 避免条件判断的复杂性:
- 如果不使用状态模式,代码中会充满大量的
if-else或switch-case条件判断。状态模式将这些判断逻辑分散到各个状态类中,避免了复杂的条件分支。
- 如果不使用状态模式,代码中会充满大量的
- 清晰的状态转换逻辑:
- 状态模式将状态和行为封装在状态类中,所有的状态转换逻辑都非常清晰。状态的变化和行为的变化是分开的,彼此不干扰。
- 遵循开闭原则:
- 新的状态和行为可以通过增加新的状态类实现,而不需要修改已有的状态逻辑,符合开闭原则,便于扩展。
总结
状态模式 是一种强大的设计模式,尤其适合在对象状态频繁变化、行为因状态不同而变化的场景中。在订单状态管理的案例中,状态模式帮助我们将订单在不同状态下的行为封装起来,使得代码更加灵活、清晰,同时提高了代码的可扩展性。
通过状态模式,开发者可以轻松应对复杂的状态转换逻辑,并在不修改已有代码的前提下添加新的状态,保证系统的灵活性和扩展性。
优点:
- 遵循开闭原则:
- 新增状态类时,不需要修改现有的上下文类或状态类,可以轻松扩展系统的状态和行为。
- 清晰的状态转换:
- 将状态转换的逻辑封装在各自的状态类中,使得状态之间的切换更加清晰且易于维护。
- 消除复杂的条件判断:
- 通过状态模式,消除了通过
if-else或switch-case来判断状态的需要。每个状态的行为封装在独立的类中。
- 通过状态模式,消除了通过
缺点:
- 类的数量增加:
- 每种状态都有一个对应的类,可能导致类的数量急剧增加,增加系统的复杂性。
- 状态切换逻辑可能复杂:
- 如果系统中状态过多,且状态间的转换规则复杂,可能会增加状态管理的难度。
相关文章:
状态模式原理剖析
《状态模式原理剖析》 状态模式(State Pattern) 是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。换句话说,当对象状态发生变化时,它的行为也会随之变化。 通过状态模式,可以消除通过 if-else…...
若伊(前后端分离)学习笔记
基础应用篇 1. 若伊搭建 若伊版本 若依官方针对不同开发需求提供了多个版本的框架,每个版本都有其独特的特点和适用场景: 前后端混合版本 :RuoYi结合了SpringBoot和Bootstrap的前端开发框架,适合快速构建传统的Web应用程序&…...
Elasticsearch学习笔记(2)
索引库操作 在Elasticsearch中,Mapping是定义文档字段及其属性的重要机制。 Mapping映射属性 type:字段数据类型 1、字符串: text:可分词的文本,适用于需要全文检索的情况。keyword:用于存储精确值&am…...
Vue devtools 插件
一、安装 去这下载https://chrome.zzzmh.cn/ 打开chrome的扩展程序 再打开开发模式 把刚才下载的拖到这里 然后把它固定到工具栏 就是这样了。 二、使用 程序通过open on live server后,打开开发者工具,找到vue就可以了。 这是代码 <div id"ap…...
Ubuntu 16.04安装填坑记录
一. 问题描述: (1)Ubuntu 16.04使用USB启动盘安装时,出现"try ubuntu without installation"或“install ubuntu”选择,Enter选择安装后,显示器黑屏无任何显示。 原因分析: 显示黑…...
python的pyinstaller
1、pyinstaller --onefile -w *.py 可以生成可执行文件 -w就是不需要有console窗体出现、 2、 console窗体会出现一些警告。 比如 Warning: QT_DEVICE_PIXEL_RATIO is deprecated. Instead use: QT_AUTO_SCREEN_SCALE_FACTOR to enable platform plugin controlled per-scre…...
Vue3(五) 组件通信大汇总
文章目录 一、props二、自定义事件三、mitt四、v-model1.v-model的本质2.v-model用在组件标签上3.更换modelValue4.更换modelValue时,可以在组件标签上多次使用v-model 五、$attrs六、$refs,与¥parent1. 回顾标签ref属性修改组件信息2. $refs实现父修改所…...
红队信息搜集扫描使用
红队信息搜集扫描使用 红队行动中需要工具化一些常用攻击,所以学习一下 nmap 等的常规使用,提供灵感 nmap 帮助 nmap --help主机扫描 Scan and no port scan(扫描但不端口扫描)。-sn 在老版本中是 -sP,P的含义是 P…...
Python自学查漏9.28
自学查漏9.28 一、环境安装&代码执行原理&变量命名 安装 Python 代码执行原理 解析(Parsing): 当你运行一个 Python 脚本时,Python 解释器首先会解析整个代码,将其转换成一种叫做“字节码”(bytecode&…...
Java文件I/O处理之RandomAccessFile【随意存取文件】
Java语言有一个处理文件输入输出的RandomAccessFile类,既可以读取文件内容,也可以向文件输出数据。 RandomAccessFile类在国内的技术文档和书籍中都翻译为“随机访问文件”类,确实令人不解。 在中文中“随机”的意思: 不设任何条…...
Android页面跳转与返回机制详解
在Android开发中,页面跳转是实现应用功能交互的重要手段之一。本文将从Activity之间的跳转、Activity与Fragment之间的跳转、Fragment之间的跳转以及页面返回的问题四个方面进行详细解析。 一、Activity之间的跳转 Activity是Android应用的基本构建块,…...
Elasticsearch学习笔记(1)
初识 Elasticsearch 认识和安装 Elasticsearch 是由 Elastic 公司开发的一套强大的搜索引擎技术,属于 Elastic 技术栈的一部分。完整的技术栈包括: Elasticsearch:用于数据存储、计算和搜索。Logstash/Beats:用于数据收集。Kib…...
react是一种语言?
React 不是一种编程语言,而是一种用于构建用户界面的 JavaScript 库。它由 Facebook 开发,并广泛用于开发单页应用程序(SPA)。React 允许你将 UI 拆分成独立的、可复用的组件,这些组件可以接收输入(称为“p…...
如何区分这个ip是真实ip,不是虚假的ip
区分一个IP地址是真实IP还是虚假IP(伪造IP)是非常重要的,特别是在网络安全、数据采集和其他与IP相关的业务场景中。虚假IP(也称为伪造IP或假冒IP)可以通过多种方式被创建,如代理、VPN、或IP欺骗(…...
【软件测试】详解软件测试中的测试级别
目录 一、测试级别二、组件测试三、开发者测试3.1测试与调试3.2 组件测试目标3.3 测试功能 四、稳健性测试4.1 效率的测试4.2 测试可维护性4.3 测试策略4.4 白盒测试 一、测试级别 软件系统通常是由许多子系统组成的,而这些子系统又是由多个组件组成的,…...
一条sql在MySQL中是怎么执行的
目录 一、MySQL总体架构二、各层的作用1、连接层2、应用层3、存储引擎层 一、MySQL总体架构 作为常问八股文,相信不少小伙伴当年都被问到过这个问题,回答这个问题我们首先得知道MySQL服务器基本架构,主要分为连接层,应用层和存储…...
Git | Dockerized GitLab 安装使用(简单实操版)
1. 详细步骤 1.1 安装启动 postgresql 服务 docker pull sameersbn/postgresql:14-20230628docker run --name gitlab-postgresql -d \--env DB_NAMEgitlabhq_production \--env DB_USERgitlab --env DB_PASSpassword \--env DB_EXTENSIONpg_trgm,btree_gist \--volume /srv/…...
SpringCloud简介 Ribbon Eureka 远程调用RestTemplate类 openfeign
〇、SpringCloud 0.区别于单体项目和soa架构,微服务架构每个服务独立,灵活。 1. spring cloud是一个完整的微服务框架,springCloud包括三个体系: spring cloud Netflix spring cloud Alibaba spring 其他 2.spring cloud 版本命名…...
微信小程序开发系列之-微信小程序性能优化
微信小程序开发系列之-微信小程序性能优化 性能优化是任何应用开发中的重要组成部分,尤其是在移动环境中。对于微信小程序而言,随着用户量的增加和应用功能的丰富,性能优化显得尤为关键。良好的性能不仅提升用户体验,还能增加用户…...
线程池面试集
目录 线程池中提交一个任务的流程是怎样的? 线程池有五种状态 如何优雅的停止一个线程? 线程池的核心线程数、最大线程数该如何设置? 如何理解Java并发中的可见性、原子性、有序性? Java死锁如何避免? 线程池中提交一个任务的流程是怎样的? 线程池有五种状态 如何优…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...
