当前位置: 首页 > news >正文

【设计模式】二十一.行为型模式之状态模式

状态模式

一. 说明

状态模式通常描述一个类不同行为的多个状态变更,对象的行为依赖它的状态,它是一种行为型模式。
状态模式可以用来消除代码中大量的if-else结构,它明确对象是有状态的、对象的不同状态对应的行为不一样、行为之间是可以切换的。简单来讲,就是对象的状态只允许在某个或某些行为下发生改变,否则不允许该行为操作对象状态。

我们用生活中购物订单的例子来说明状态模式,图示如下:
在这里插入图片描述
从图示中可以看出:

  • 未支付的订单只能被用户做支付或者取消操作
  • 已支付的订单只能被用户发货前催单 (按催单对象不同分为两种催单形式)
  • 已发货的订单只能被用户发货后催单
  • 已签收的订单只能被用户评价或删除
  • 已删除的订单用户已经看不到了,无法做任何操作

总的来说就是用户订单的某个状态只能被用户的某个对应行为所改变,而其他行为是无法操作的。

二.应用场景

  1. 线上购物时的订单状态随着不同行为而变化
  2. 营销活动的上线审批流程,活动也是有状态变化的
  3. 游戏时控制的角色在不同环境下有不同的状态(buff加成或debuff时角色属性有不同的变化)

三.代码示例

以购物下单为例,我们在OrderService里实现几个行为:支付、取消订单、催单、判断是否可评价、删除订单这几个行为,按一般的写法是这样的:

先创建OrderService接口,提供几个方法

public interface OrderService {/*** 订单支付*/boolean pay(OrderDTO orderDTO);/*** 订单取消*/boolean cancel(OrderDTO orderDTO);/*** 催单*/boolean reminder(OrderDTO orderDTO);/*** 订单评价校验*/boolean isEvaluable(OrderDTO orderDTO);/*** 删除订单*/boolean delete(OrderDTO orderDTO);
}

实现Service接口,每个行为都需要判断在特定状态下才能操作

public class OrderServiceImpl implements OrderService {@Overridepublic boolean pay(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.UNPAID) {System.out.println("pay-订单支付流程");orderDTO.setState(StateEnum.PAID);System.out.println("pay-订单支付成功");return true;}System.out.println("pay-该订单无法支付,当前状态为:" + orderDTO.getState().getName());return false;}@Overridepublic boolean cancel(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.UNPAID) {System.out.println("cancel-订单取消流程");orderDTO.setState(StateEnum.CANCEL);System.out.println("cancel-订单取消成功");return true;}System.out.println("cancel-该订单无法取消,当前状态为:" + orderDTO.getState().getName());return false;}@Overridepublic boolean reminder(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.PAID) {System.out.println("reminder-订单催单成功,催单对象为卖家");return true;}if (orderDTO.getState() == StateEnum.DELIVERED) {System.out.println("reminder-订单催单成功,催单对象为物流");return true;}System.out.println("reminder-该订单无法催单,当前状态为:" + orderDTO.getState().getName());return false;}@Overridepublic boolean isEvaluable(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.SIGNED) {System.out.println("evaluable-订单可以评价");return true;}System.out.println("evaluable-该订单无法评价,当前状态为:" + orderDTO.getState().getName());return false;}@Overridepublic boolean delete(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.SIGNED) {System.out.println("delete-订单删除流程");orderDTO.setState(StateEnum.DELETED);System.out.println("delete-订单删除成功");return true;}System.out.println("delete-订单无法删除,当前状态为:" + orderDTO.getState().getName());return false;}
}

编写测试代码

public static void main(String[] args) {OrderDTO orderDTO = new OrderDTO();orderDTO.setOrderId("202400001");orderDTO.setState(StateEnum.UNPAID);OrderService service = new OrderServiceImpl();service.pay(orderDTO);service.pay(orderDTO);service.cancel(orderDTO);service.reminder(orderDTO);orderDTO.setState(StateEnum.DELIVERED);service.reminder(orderDTO);orderDTO.setState(StateEnum.SIGNED);service.isEvaluable(orderDTO);service.delete(orderDTO);}

在这里插入图片描述

如上图代码所示,这些代码的特点是,OrderService下的每一方法(即行为),都需要考虑订单的各个状态,业务逻辑相对来说复杂一些,如果要增加一个新的订单状态,每个方法都要按需调整,扩展性不高,不符合开闭原则。

我们在SpringBoot环境中使用状态模式重构以上的订单流程:

首先需要引入另一组service,用来表示订单状态的一组业务,命名为OrderStateService,提供与OrderService一致的行为方法

public interface IOrderStateService {StateEnum getState();/*** 订单支付*/boolean pay(OrderDTO orderDTO);/*** 订单取消*/boolean cancel(OrderDTO orderDTO);/*** 催单*/boolean reminder(OrderDTO orderDTO);/*** 订单评价校验*/boolean isEvaluable(OrderDTO orderDTO);/*** 删除订单*/boolean delete(OrderDTO orderDTO);
}

再编写抽象业务,实现接口的方法,提供默认的实现,默认实现都是该订单状态不支持该行为操作,我们把真正的行为实现落地到具体的实现类上。

@Slf4j
public abstract class AbstractOrderStateService implements IOrderStateService {@Overridepublic boolean pay(OrderDTO orderDTO) {log.info("pay-该订单无法支付,当前状态为:{}", orderDTO.getState().getName());return false;}@Overridepublic boolean cancel(OrderDTO orderDTO) {log.info("cancel-该订单无法取消,当前状态为:{}", orderDTO.getState().getName());return false;}@Overridepublic boolean reminder(OrderDTO orderDTO) {log.info("reminder-该订单无法催单,当前状态为:{}", orderDTO.getState().getName());return false;}@Overridepublic boolean isEvaluable(OrderDTO orderDTO) {log.info("evaluable-该订单无法评价,当前状态为:{}", orderDTO.getState().getName());return false;}@Overridepublic boolean delete(OrderDTO orderDTO) {log.info("delete-该订单无法删除,当前状态为:{}", orderDTO.getState().getName());return false;}
}

再重写每个状态对应允许的行为操作,一旦重写即表示该状态允许该行为操作

//未支付状态重写支付、取消、删除方法
@Slf4j
@Service
public class UnpaidOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.UNPAID;}@Overridepublic boolean pay(OrderDTO orderDTO) {log.info("pay-订单支付成功,当前订单状态:{}", orderDTO.getState().getName());orderDTO.setState(StateEnum.PAID);return true;}@Overridepublic boolean cancel(OrderDTO orderDTO) {log.info("cancel-订单取消成功,当前订单状态:{}", orderDTO.getState().getName());orderDTO.setState(StateEnum.CANCEL);return true;}@Overridepublic boolean delete(OrderDTO orderDTO) {log.info("delete-订单取消成功,当前订单状态:{}", orderDTO.getState().getName());orderDTO.setState(StateEnum.DELETED);return true;}
}
//已支付状态重写催单方法
@Slf4j
@Service
public class PaidOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.PAID;}@Overridepublic boolean reminder(OrderDTO orderDTO) {log.info("发货前催单成功, 催单对象为卖家,当前订单状态:{}", orderDTO.getState().getName());return true;}
}
//已发货状态重写催单方法
@Slf4j
@Service
public class DeliveredOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.DELIVERED;}@Overridepublic boolean reminder(OrderDTO orderDTO) {log.info("发货后催单成功, 催单对象为物流,当前订单状态:{}", orderDTO.getState().getName());return true;}
}
//已签收状态重写判断评价和删除方法
@Slf4j
@Service
public class SignedOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.SIGNED;}@Overridepublic boolean isEvaluable(OrderDTO orderDTO) {log.info("订单允许评价,当前订单状态:{}", orderDTO.getState().getName());return true;}@Overridepublic boolean delete(OrderDTO orderDTO) {log.info("订单删除成功,当前订单状态:{}", orderDTO.getState().getName());orderDTO.setState(StateEnum.DELETED);return true;}
}
//已删除状态不做任何操作
@Service
public class DeletedOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.DELETED;}
}
//已取消状态不做任何操作
@Service
public class CancelOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.CANCEL;}
}

编写获取具体StateService的工厂类

@Component
public class OrderStateFactory {private final Map<StateEnum, IOrderStateService> stateMap = new HashMap<>();@Resourceprivate Set<IOrderStateService> orderStateServiceSet;@PostConstructpublic void init() {orderStateServiceSet.forEach(service -> {stateMap.put(service.getState(), service);});}public IOrderStateService getOrderStateService(StateEnum state) {return stateMap.get(state);}}

改造OrderService的实现类

@Service
public class OrderServiceImpl implements OrderService {@Resourceprivate OrderStateFactory orderStateFactory;@Overridepublic boolean pay(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).pay(orderDTO);}@Overridepublic boolean cancel(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).cancel(orderDTO);}@Overridepublic boolean reminder(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).reminder(orderDTO);}@Overridepublic boolean isEvaluable(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).isEvaluable(orderDTO);}@Overridepublic boolean delete(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).delete(orderDTO);}
}

编写测试代码

public static void test(){OrderServiceImpl service = (OrderServiceImpl) applicationContext.getBean("orderServiceImpl");OrderDTO orderDTO = new OrderDTO();orderDTO.setOrderId("202400001");orderDTO.setState(StateEnum.UNPAID);service.pay(orderDTO);service.pay(orderDTO);service.cancel(orderDTO);service.reminder(orderDTO);orderDTO.setState(StateEnum.DELIVERED);service.reminder(orderDTO);orderDTO.setState(StateEnum.SIGNED);service.isEvaluable(orderDTO);service.delete(orderDTO);}

在这里插入图片描述

可以看到,重构后实现了相同的业务功能,并且完全干掉了if-else的判断代码。后续新增某个订单状态,我们只需要扩展StateService的具体状态实现即可。虽然整个模块的类增加了,略显复杂,但我们牺牲复杂性去换取高可维护性和扩展性是相当值得的。

四. 总结

在状态模式中,我们把每一种状态都独立成一个类进行处理,这满足了单一职责和开闭原则。状态模式强调的是行为和状态的对应,只有在特定的业务场景下使用它,在通常三层架构的web开发中,对象的状态和对象行为一般都是分离的,所以在开发中我们使用地并不多。

状态模式和状态机也是有关系的,状态机类似一种开发引擎便于我们在开发中使用,它同样采用的是状态模式的思想实现的。

相关文章:

【设计模式】二十一.行为型模式之状态模式

状态模式 一. 说明 状态模式通常描述一个类不同行为的多个状态变更&#xff0c;对象的行为依赖它的状态&#xff0c;它是一种行为型模式。 状态模式可以用来消除代码中大量的if-else结构&#xff0c;它明确对象是有状态的、对象的不同状态对应的行为不一样、行为之间是可以切…...

微服务实战系列之Dubbo(下)

前言 眼看着2023即将走远&#xff0c;心里想着似乎还有啥&#xff0c;需要再跟各位盆友叨叨。这不说曹操&#xff0c;曹操就来了。趁着上一篇Dubbo博文的余温尚在&#xff0c;博主兴匆匆地“赶制”了Dubbo的下集&#xff0c;以飨读者。 上一篇博主依然从Dubbo的内核出发&#…...

《剑指offer》数学第二题:求1+2+3+...+n

题目描述&#xff1a; 求123...n&#xff0c;要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句&#xff08;A?B:C&#xff09;。思路&#xff1a; 我们可以用递归和短路运算符来进行运算&#xff0c;具体代码如下。 代码实现&#xff1a; pac…...

阿里云服务器3M固定带宽速度快吗?

阿里云服务器3M固定带宽是什么意思&#xff1f;速度快吗&#xff1f;3M固定带宽是指云服务器的公网带宽&#xff0c;用于在外网提供服务的&#xff0c;3M带宽的下载速度是384KB/秒&#xff0c;上传速度是1280KB/秒&#xff0c;对于个人博客或流量不多的企业官网速度还是挺快的&…...

美易官方:新年伊始美企狂发450多亿美元债券

新年伊始&#xff0c;美国企业疯狂发行了价值超过450亿美元的债券&#xff0c;创下了历史新高。这一数字比去年同期增长了约50%&#xff0c;凸显出美国企业在全球经济增长放缓的背景下&#xff0c;依然保持着强劲的融资需求和信心。美国企业借款人周三将发行近160亿美元高评级债…...

[云原生] Go web工作流程

web工作流程 Web服务器的工作原理可以简单地归纳为 客户机通过TCP/IP协议建立到服务器的TCP连接客户端向服务器发送HTTP协议请求包&#xff0c;请求服务器里的资源文档服务器向客户机发送HTTP协议应答包&#xff0c;如果请求的资源包含有动态语言的内容&#xff0c;那么服务器…...

【PostgreSQL】约束-主键

【PostgreSQL】约束链接 检查 唯一 主键 外键 排他 主键 主键&#xff08;Primary Key&#xff09;是数据库表中用于唯一标识每一行记录的字段。主键具有以下特点&#xff1a; 唯一性&#xff1a;每个主键值在表中是唯一的&#xff0c;不允许出现重复值。非空性&#xff1a…...

IDEA 控制台中文乱码问题解决方法(UTF-8 编码)

设置 IDEA 编码格式 1&#xff1a;打开 IntelliJ IDEA>File>Setting>Editor>File Encodings&#xff0c;将 Global Encoding、Project Encoding、Default encodeing for properties files 这三项都设置成 UTF-8 2&#xff1a;将 vm option 参数改为&#xff1a; -…...

ssm基于BS的仓库在线管理系统的设计与实现论文

摘 要 如今的时代&#xff0c;是有史以来最好的时代&#xff0c;随着计算机的发展到现在的移动终端的发展&#xff0c;国内目前信息技术已经在世界上遥遥领先&#xff0c;让人们感觉到处于信息大爆炸的社会。信息时代的信息处理肯定不能用之前的手工处理这样的解决方法&#x…...

鸿蒙HarmonyOs:为什么不支持热更新?

学习了一段时间的鸿蒙开发&#xff0c;发现鸿蒙开发还是比较简单的&#xff0c;今天突然心血来潮&#xff0c;研究了一下鸿蒙热更新&#xff0c;最终得出的结论是鸿蒙暂时不支持热更新。 鸿蒙app开发主要是利用的ArkTs语言&#xff0c;ArkTs又是基于TypeScript语言的&#xff0…...

修改 Ubuntu 的配置

目录 一、修改地址 1. 修改本机IP 二、修改网关 1. 查看网关地址 2. 设置默认网关 三、重启网络 1. 重启网络 2. 刷新网络 四、修改主机名 1. 查看主机名 2. 修改主机名 一、修改地址 1. 修改本机IP sudo ifconfig en…...

虹科方案|从困境到突破:TigoLeap方案引领数据采集与优化

导读&#xff1a;在数字化工厂和智能制造的时代&#xff0c;数据已经成为优化机器和流程的关键。然而&#xff0c;如何高效地收集和处理这些数据&#xff0c;特别是在开发、部署和生产阶段&#xff0c;仍是企业面临的一大挑战。虹科TigoLeap平台&#xff0c;作为一款引领行业变…...

【教学类-43-02】20231226 九宫格数独2.0(n=9)(ChatGPT AI对话大师生成 回溯算法)

作品展示&#xff1a; 背景需求&#xff1a; 大4班20号说&#xff1a;我不会做这种&#xff08;九宫格&#xff09;&#xff0c;我做的是小格子的&#xff0c; 他把手工纸翻过来&#xff0c;在反面自己画了矩阵格子。向我展示&#xff1a; “我会做这种&#xff01;” 原来他…...

麒麟Kylin服务器版-破解root密码

一、单用户模式修改root密码 1.重启服务器系统后&#xff0c;将光标移动到第二项&#xff0c;按【e】键进入用户登录页面。 2.在【username】下方所在行输入root名称&#xff0c;【password】下方所在行输入密码Kylin123123后&#xff0c;进入编辑模式。代码如下&#xff1a; …...

cnPuTTY 0.80.0.1—PuTTY Release 0.80中文版本简单说明~~

2023-12-18 官方发布了PuTTY 0.80本次发布主要是针对Terrapin攻击(CVE-2023-48795)的修改发布。 更多详细的内容请查看PuTTY Change Log。 有关Terrapin攻击可用简单参考&#xff1a;警告&#xff01;&#xff01;&#xff01;Terrapin攻击(CVE-2023-48795)~~~ 为了缓解此漏洞…...

向爬虫而生---Redis 拓宽篇1 < pipeline传输效率>

前言: 都知道,Redis是一款高效的内存数据库;每条命令都能很快响应,但是如果我们把服务器布在网络上,每次一个命令来回传送也是需要花费时间的; pipeline传输技术则是进一步提高Redis的性能和传输效率的一种方法。 正文: pipeline与普通命令发送方式的区别 Pipeline是一种机制&…...

Unity Hub 无法激活许可证

烦死了~ &#x1f635;‍&#x1f4ab; 卸载UnityHub, 安装旧版本&#x1f448;激活许可证&#xff0c; 如果出现旧版本无法识别Editor的情况需要卸载了再装最新版本的UnityHub...

数据分析求职-如何准备

今天咱们来聊一聊数据分析岗位求职到底需要准备什么&#xff1f;什么时间准备&#xff1f;该如何准备&#xff1f; 1. 求职时间轴 上图很清楚地把求职时间轴展示出来了&#xff0c;重复的话也不多说了&#xff0c;有两点想和同学们重点提下&#xff1a; 1&#xff09;一定一定…...

新手能掌握 PyTorch 的填充技术:深入理解反射、复制、零值和常数填充

目录 torch.nn子模块详解 nn.ReflectionPad1d 参数说明&#xff1a; 形状&#xff08;Shape&#xff09;&#xff1a; 使用示例&#xff1a; 注意事项&#xff1a; nn.ReflectionPad2d 参数说明&#xff1a; 形状&#xff08;Shape&#xff09;&#xff1a; 使用示例…...

地震烈度速报与预警工程成功案例的经验分享 | TDengine 技术培训班第一期成功落地

近日&#xff0c;涛思数据在成都开设了“国家地震烈度速报与预警工程数据库 TDengine、消息中间件 TMQ 技术培训班”&#xff0c;这次培训活动共分为三期&#xff0c;而本次活动是第一期。其目标是帮助参与者深入了解 TDengine 和 TMQ 的技术特点和应用场景&#xff0c;并学习如…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...