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

状态设计模式

状态设计模式是一种行为设计模式,它允许对象在其内部状态更改时更改其行为,就像它在运行时切换到不同的类一样。

它在以下情况下特别有用:
对象可以处于许多不同状态之一,每种状态都有不同的行为。
对象的行为取决于当前上下文,并且该上下文会随着时间的推移而变化。
你希望避免检查每个可能状态的大型、整体式或语句。if-elseswitch
当面对这种情况时,开发人员通常首先在类中使用条件逻辑来切换基于状态变量的行为。

例如,类 可能会使用 块来确定要执行的作,具体取决于它是处于“草稿”、“审阅”还是“已发布”状态。Documentif-else

但随着状态数量的增长,这种方法变得难以扩展、难以测试,并且违反了开放/封闭原则——任何新状态都需要修改现有逻辑,从而增加了破坏当前功能的风险。

状态模式通过将每个状态封装到自己的类中,并让上下文对象将行为委托给当前状态对象来解决这个问题。这使您的代码更易于扩展、重用和维护,而不会因条件而使核心逻辑变得混乱。

让我们通过一个真实世界的示例来了解如何应用状态模式以干净、可扩展和面向对象的方式管理动态行为。

问题:管理自动售货机状态
想象一下,您正在构建一个简单的自动售货机系统。从表面上看,这似乎是一项简单的任务:接受资金,分配产品,然后回到闲置状态。

但在幕后,机器的行为需要根据其当前状态而变化。

在任何给定时间,自动售货机只能处于一种状态,例如:
IdleState – 等待用户输入(未选择任何内容,未插入任何资金)。
ItemSelectedState – 已选择项目,等待付款。
HasMoneyState – 已插入资金,等待分配所选项目。
DispensingState – 机器正在主动分配项目。

该机器支持一些面向用户的作:
selectItem(String itemCode)– 选择要购买的商品
insertCoin(double amount)– 插入所选项目的付款
dispenseItem()– 触发物品分配过程
这些方法中的每一种都应根据计算机的当前状态以不同的方式运行。

例如:
在 机器打开时 调用(未选择任何项目,未插入任何资金)不应执行任何作或显示错误。dispenseItem()IdleState
在选择项目之前调用可能会被禁止或排队。insertCoin()
应 忽略或推迟期间调用,直到项目被分配。selectItem()DispensingState

天真的方法
一种常见但有缺陷的方法是 使用 or 语句在整体类中手动管理状态转换:VendingMachineif-elseswitch

public class VendingMachine {private enum State {IDLE, ITEM_SELECTED, HAS_MONEY, DISPENSING}private State currentState = State.IDLE;private String selectedItem = "";private double insertedAmount = 0.0;public void selectItem(String itemCode) {switch (currentState) {case IDLE:selectedItem = itemCode;currentState = State.ITEM_SELECTED;break;case ITEM_SELECTED:System.out.println("Item already selected");break;case HAS_MONEY:System.out.println("Payment already received for item");break;case DISPENSING:System.out.println("Currently dispensing");break;}}public void insertCoin(double amount) {switch (currentState) {case IDLE:System.out.println("No item selected");break;case ITEM_SELECTED:insertedAmount = amount;System.out.println("Inserted $" + amount + " for item");currentState = State.HAS_MONEY;break;case HAS_MONEY:System.out.println("Money already inserted");break;case DISPENSING:System.out.println("Currently dispensing");break;}}public void dispenseItem() {switch (currentState) {case IDLE:System.out.println("No item selected");break;case ITEM_SELECTED:System.out.println("Please insert coin first");break;case HAS_MONEY:System.out.println("Dispensing item '" + selectedItem);currentState = State.DISPENSING;// Simulate delay and completiontry {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("Item dispensed successfully.");resetMachine();break;case DISPENSING:System.out.println("Already dispensing. Please wait.");break;}}private void resetMachine() {selectedItem = "";insertedAmount = 0.0;currentState = State.IDLE;}
}

这种方法有什么问题?
虽然使用 with 语句适用于小型、可预测的系统,但这种方法不能很好地扩展。enumswitch

  1. 1. 代码混乱
    所有与状态相关的逻辑都塞进一个类 () 中,导致每个方法( 、 、 等)都出现大而重复的块。VendingMachineswitchif-elseselectItem()insertCoin()dispenseItem()

这导致:
难以阅读和推理的代码
跨多个方法重复检查状态
多个开发人员接触同一文件时的逻辑脆弱

  1. 2. 难以扩展
    假设你想引入新的状态,例如:
    OutOfStockState– 当所选商品售罄时
    MaintenanceState– 机器正在维修时
    要支持这些,您需要:
    更新 每个方法中的每个开关块
    在多个位置添加逻辑
    破坏现有功能的风险
    这违反了开放/封闭原则——当系统应该开放扩展时,系统可以进行修改。
  2. 3. 违反单一责任原则
    该 班现在负责:VendingMachine
    管理状态转换
    实施业务规则
    执行特定于状态的逻辑
    这种紧密耦合使该类成为单片、难以测试且耐变化。

我们真正需要什么
我们需要将与每个状态关联的行为封装到它自己的类中——这样自动售货机就可以将工作委托给当前状态对象,而不是在内部管理它。

这将使我们能够:
避免疯狂的开关情况
添加或删除状态而不修改核心类
保持每个状态的逻辑隔离和可重用
这正是状态设计模式所实现的。

状态模式
State 模式允许对象(Context)在其内部状态更改时更改其行为。该对象似乎更改了其类,因为它的行为现在已委托给不同的状态对象。

状态模式不是将特定于状态的行为嵌入上下文类本身,而是将该行为提取到单独的类中,每个类代表一个不同的状态。上下文对象保存对状态对象的引用,并将其作委托给它。

这会产生更干净、更模块化和可扩展的代码。
定义一个 State 接口(或抽象类),该接口声明表示 Context 可以执行的作的方法。
创建 ConcreteState 类,每个类都实现 State 接口。每个 ConcreteState 类都实现特定于 Context 的特定状态的行为。
Context类维护定义其当前状态的ConcreteState子类的实例。
在 Context 上调用作时,它会将该作委托给其当前 State 对象。
ConcreteState 对象通常负责将 Context 转换为新状态。

类图

 

  1. 1. 状态接口(例如MachineState)
    声明与上下文支持的作相对应的方法(例如,, ,)。selectItem()insertCoin()dispenseItem()
    这些方法通常将上下文作为参数,因此状态可以触发转换或作上下文数据。
    作为所有具体状态的共同合同。
  2. 2. 具体状态(例如, IdleStateItemSelectedState)
    实现接口 。State
    为 每个作定义特定于状态的行为。
    通常负责 在发生特定作时将上下文转换为另一种状态。
    还可以包括特定于状态的逻辑(例如,验证、消息传递)。
  3. 3. 上下文(例如VendingMachine)
    维护对当前对象的引用 。State
    定义每个作(、 等)的方法。selectItem()insertCoin()
    委托对当前状态的调用 — 状态对象处理逻辑。
    提供一种方法,例如允许状态之间的转换。setState()

实现状态模式
我们将应用状态模式来分离关注点,并使自动售货机更易于管理和扩展,而不是使用 or 语句将状态转换和行为硬编码为单个整体类。if-elseswitch

第一步是定义一个 接口,用于声明自动售货机支持的所有作。MachineState

  1. 1. 定义状态接口
    该接口表示所有州都必须遵循的契约。它声明与自动售货机面向用户的作相对应的方法。
public interface MachineState {void selectItem(VendingMachine context, String itemCode);void insertCoin(VendingMachine context, double amount);void dispenseItem(VendingMachine context);
}

每个状态都将实现此接口,定义自动售货机在该状态下的行为方式。

  1. 2. 实现具体状态类
    每个状态类将实现 接口并为每个作定义其行为。MachineState

🟡 空闲状态

public class IdleState implements MachineState {@Overridepublic void selectItem(VendingMachine context, String itemCode) {System.out.println("Item selected: " + itemCode);context.setSelectedItem(itemCode);context.setState(new ItemSelectedState());}@Overridepublic void insertCoin(VendingMachine context, double amount) {System.out.println("Please select an item before inserting coins.");}@Overridepublic void dispenseItem(VendingMachine context) {System.out.println("No item selected. Nothing to dispense.");}
}

🟠 ItemSelected状态

public class ItemSelectedState implements MachineState {@Overridepublic void selectItem(VendingMachine context, String itemCode) {System.out.println("Item already selected: " + context.getSelectedItem());}@Overridepublic void insertCoin(VendingMachine context, double amount) {System.out.println("Inserted $" + amount + " for item: " + context.getSelectedItem());context.setInsertedAmount(amount);context.setState(new HasMoneyState());}@Overridepublic void dispenseItem(VendingMachine context) {System.out.println("Insert coin before dispensing.");}
}

🟢 有钱州

public class HasMoneyState implements MachineState {@Overridepublic void selectItem(VendingMachine context, String itemCode) {System.out.println("Cannot change item after inserting money.");}@Overridepublic void insertCoin(VendingMachine context, double amount) {System.out.println("Money already inserted.");}@Overridepublic void dispenseItem(VendingMachine context) {System.out.println("Dispensing item: " + context.getSelectedItem());context.setState(new DispensingState());// Simulate dispensingtry { Thread.sleep(1000); } catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("Item dispensed successfully.");context.reset();}
}

🔵 分配状态

public class DispensingState implements MachineState {@Overridepublic void selectItem(VendingMachine context, String itemCode) {System.out.println("Please wait, dispensing in progress.");}@Overridepublic void insertCoin(VendingMachine context, double amount) {System.out.println("Please wait, dispensing in progress.");}@Overridepublic void dispenseItem(VendingMachine context) {System.out.println("Already dispensing. Please wait.");}
}
  1. 3. 实现上下文 (VendingMachine)
    类(我们的上下文)将维护对当前状态的引用,并将所有作委托给它。VendingMachine
public class VendingMachine {private MachineState currentState;private String selectedItem;private double insertedAmount;public VendingMachine() {this.currentState = new IdleState(); // Initial state}public void setState(MachineState newState) {this.currentState = newState;}public void setSelectedItem(String itemCode) {this.selectedItem = itemCode;}public void setInsertedAmount(double amount) {this.insertedAmount = amount;}public String getSelectedItem() {return selectedItem;}public double getInsertedAmount() {return insertedAmount;}public void selectItem(String itemCode) {currentState.selectItem(this, itemCode);}public void insertCoin(double amount) {currentState.insertCoin(this, amount);}public void dispenseItem() {currentState.dispenseItem(this);}public void reset() {this.selectedItem = "";this.insertedAmount = 0.0;this.currentState = new IdleState();}
}

客户端代码

public class VendingMachineApp {public static void main(String[] args) {VendingMachine vm = new VendingMachine();vm.insertCoin(1.0); // Invalid in IdleStatevm.selectItem("A1");vm.insertCoin(1.5);vm.dispenseItem();System.out.println("\n--- Second Transaction ---");vm.selectItem("B2");vm.insertCoin(2.0);vm.dispenseItem();}
}

通过使用状态模式,我们将僵化的、条件密集的实现转变为一个干净、灵活的架构,其中行为和转换被明确定义、解耦且易于维护。

http://www.xdnf.cn/news/18822.html

相关文章:

  • 构建面向人工智能决策的世界模型引擎所需的基本知识体系
  • 如何在GitHub找到10k+个stars的仓库
  • podman启动mongdb的container因为权限问题导致changing ownership和读取storage.bson失败的解决方法
  • CMake构建学习笔记20-iconv库的构建
  • 算法概述篇
  • 游戏空间划分技术
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(20):文法+单词第7回2
  • 广告推荐模型1:逻辑回归(Logistic Regression,LR)
  • 如何拯救一家濒临破产的科技公司?
  • 技术总结:AArch64架构下Jenkins Agent(RPM容器编译节点)掉线问题分析与排查
  • KubeBlocks for Oracle 容器化之路
  • 【RAGFlow代码详解-30】构建系统和 CI/CD
  • 微服务-28.配置管理-共享配置
  • poi生成word固定表格列宽
  • TensorFlow 面试题及详细答案 120道(61-70)-- 高级特性与工具
  • css3背景线性渐变:linear-gradient
  • 【密集目标检测】停车场车辆(车位)识别数据集:12k+图像,yolo标注
  • 04 网络信息内容安全--入侵检测技术
  • 依托边缘计算方案,移动云全面化解算力、效率、安全平衡难题
  • from中烟科技翼支付 面试题2
  • 高频面试题:说一下线程池吧?(线程池原理,核心参数,创建方式,应用场景都要说到才能让面试官心服口服)
  • Pytorch深度学习(小土堆)
  • Java 企业应用单点登录(SSO)实现方案详解
  • 如何对springboot mapper 编写单元测试
  • Ansible 文件管理与 Jinja2 模板全解析:从模块应用到动态配置生成
  • 由倍讯科技研制的CCLinkIE转ModbusTCP网关,可达成与脉冲计数器的连接
  • JVM分层编译深度解析:完整机制与实践指南
  • 《零基础入门AI:长短期记忆网络(LSTM)与门控循环单元(GRU)(原理、结构与实现)》
  • 【大前端】实现一个前端埋点SDK,并封装成NPM包
  • 【机械故障】旋转机械故障引起的振动信号调制效应概述