狀態模式:有限狀態機在電商訂單系統中的設計與實現

狀態模式:有限狀態機在電商訂單系統中的設計與實現

一、模式核心:用狀態切換驅動行為變化

在電商訂單系統中,訂單狀態會隨著用戶操作動態變化:「已創建」的訂單支付后變為「已支付」,發貨后變為「已發貨」,不同狀態下的操作權限和業務邏輯差異巨大。傳統方式通過大量if-else判斷狀態,導致代碼臃腫且難以維護。狀態模式(State Pattern) 通過將狀態封裝為獨立類,使對象在不同狀態下自動切換行為,核心解決:

  • 狀態驅動行為:不同狀態對應不同操作邏輯,避免海量條件判斷
  • 狀態轉換可控:集中管理狀態遷移規則,確保狀態變化符合業務流程

核心思想與 UML 類圖

img

二、核心實現:構建可擴展的訂單狀態機

1. 定義狀態接口(封裝狀態相關操作)

public interface OrderState {// 支付操作:不同狀態下支付邏輯不同void pay(OrderContext context);// 發貨操作:僅特定狀態允許發貨void deliver(OrderContext context);// 取消操作:不同狀態下取消流程不同void cancel(OrderContext context);
}

2. 實現具體狀態類(封裝各狀態的行為)

已創建狀態(允許支付和取消)
public class CreatedState implements OrderState {@Overridepublic void pay(OrderContext context) {System.out.println("訂單創建狀態:執行支付流程...");context.setCurrentState(new PaidState()); // 切換到已支付狀態System.out.println("狀態變更:已創建 → 已支付");}@Overridepublic void deliver(OrderContext context) {throw new IllegalStateException("錯誤:未支付訂單不能發貨");}@Overridepublic void cancel(OrderContext context) {System.out.println("訂單創建狀態:執行取消流程(無需扣款)");context.setCurrentState(new CanceledState()); // 切換到已取消狀態}
}
已支付狀態(允許發貨和取消)
public class PaidState implements OrderState {@Overridepublic void pay(OrderContext context) {throw new IllegalStateException("錯誤:訂單已支付,請勿重復支付");}@Overridepublic void deliver(OrderContext context) {System.out.println("訂單支付狀態:執行發貨流程...");context.setCurrentState(new DeliveredState()); // 切換到已發貨狀態System.out.println("狀態變更:已支付 → 已發貨");}@Overridepublic void cancel(OrderContext context) {System.out.println("訂單支付狀態:執行取消流程(需退款)");context.setCurrentState(new CanceledState());}
}

3. 上下文類(管理狀態切換與狀態相關數據)

public class OrderContext {private OrderState currentState;private final String orderId;public OrderContext(String orderId) {this.orderId = orderId;this.currentState = new CreatedState(); // 初始狀態為已創建}// 狀態切換入口public void setCurrentState(OrderState state) {this.currentState = state;}// 對外暴露的業務操作,委托給當前狀態處理public void pay() {currentState.pay(this);}public void deliver() {currentState.deliver(this);}public void cancel() {currentState.cancel(this);}
}

4. 客戶端調用示例(狀態流轉演示)

public class ClientDemo {public static void main(String[] args) {OrderContext order = new OrderContext("ORDER_1001");// 支付操作:創建狀態 → 支付狀態order.pay(); // 輸出:支付流程 & 狀態變更// 發貨操作:支付狀態 → 發貨狀態order.deliver(); // 輸出:發貨流程 & 狀態變更// 嘗試重復支付(已支付狀態不允許)try {order.pay();} catch (IllegalStateException e) {System.out.println("異常:" + e.getMessage()); // 輸出錯誤信息}}
}

三、進階:構建健壯的狀態機框架

1. 狀態工廠(集中管理狀態實例)

public class OrderStateFactory {private static final Map<StateType, OrderState> STATE_POOL = new EnumMap<>(StateType.class);static {STATE_POOL.put(StateType.CREATED, new CreatedState());STATE_POOL.put(StateType.PAID, new PaidState());// 注冊所有狀態類}public static OrderState getState(StateType type) {return STATE_POOL.get(type);}
}// 使用枚舉定義狀態類型(避免魔法值)
enum StateType {CREATED, PAID, DELIVERED, CANCELED
}

2. 狀態轉換校驗(防止非法狀態遷移)

public abstract class BaseOrderState implements OrderState {// 定義合法的狀態轉換規則protected abstract Set<StateType> allowedNextStates();@Overridepublic final void transitionTo(OrderContext context, StateType nextState) {if (allowedNextStates().contains(nextState)) {context.setCurrentState(OrderStateFactory.getState(nextState));} else {throw new IllegalArgumentException("非法狀態轉換:當前狀態" + getCurrentState() + "不能轉換為" + nextState);}}
}// 具體狀態類實現合法轉換規則
public class CreatedState extends BaseOrderState {@Overrideprotected Set<StateType> allowedNextStates() {return Set.of(StateType.PAID, StateType.CANCELED); // 僅允許支付或取消}
}

3. 可視化狀態機(狀態流轉圖)

支付
取消
發貨
取消
確認收貨
終止
已創建
已支付
已取消
已發貨
已完成
已關閉

四、框架與源碼中的狀態模式實踐

1. Spring State Machine(專業狀態機框架)

  • 核心組件:

    • StateMachine:管理狀態和轉換
    • Transition:定義狀態轉換條件(如支付成功觸發狀態變更)
  • 使用示例:

    // 定義訂單狀態和事件
    StateMachine<OrderState, OrderEvent> stateMachine = StateMachineBuilder.<OrderState, OrderEvent>builder().withStates().initial(OrderState.CREATED).state(OrderState.PAID).end(OrderState.CANCELED, OrderState.COMPLETED).withTransitions().from(OrderState.CREATED).to(OrderState.PAID).on(OrderEvent.PAY).build();stateMachine.sendEvent(OrderEvent.PAY); // 觸發狀態轉換
    

2. MyBatis 事務狀態管理

  • Executor接口根據事務狀態(自動提交 / 手動提交)切換執行邏輯
  • 通過BaseExecutor的子類(如SimpleExecutorBatchExecutor)實現不同狀態下的行為

3. TCP 連接狀態(Java NIO 實現)

  • SelectionKey的狀態(連接、可讀、可寫)通過狀態模式管理事件分發
  • 避免大量if (key.isReadable())類型的條件判斷

五、避坑指南:正確使用狀態模式的 3 個要點

1. 避免狀態爆炸(控制狀態數量)

  • ? 反模式:為每個細小狀態創建獨立類(如訂單的「支付中」「發貨中」)
  • ? 最佳實踐:
    • 合并相似狀態(如「待審核」「審核中」合并為「審核狀態」)
    • 使用狀態工廠 + 枚舉統一管理狀態實例

2. 處理狀態轉換的原子性

  • 在分布式系統中,狀態變更需結合分布式鎖或事務保證原子性
// 分布式場景下的狀態轉換(偽代碼)
public void safeTransition(OrderContext context, StateType nextState) {String lockKey = "order_state_lock:" + context.getOrderId();try (RedissonLock lock = redisson.getLock(lockKey)) {lock.lock();currentState.transitionTo(context, nextState);}
}

3. 狀態類的職責單一性

  • 狀態類應專注于狀態相關行為,避免包含業務邏輯之外的代碼
  • 復雜業務邏輯可提取為獨立服務(如PaymentServiceDeliveryService

六、總結:何時該用狀態模式?

適用場景核心特征典型案例
對象狀態驅動行為不同狀態下操作邏輯差異大,且狀態可枚舉訂單狀態機、電梯控制系統、工作流引擎
狀態轉換規則復雜需要集中管理合法的狀態遷移路徑游戲角色狀態(戰斗 / 待機 / 死亡)、設備狀態(開機 / 待機 / 關機)
避免海量條件判斷拒絕if-else地獄,追求代碼可維護性編譯器狀態(詞法分析 / 語法分析 / 語義分析)

狀態模式通過「狀態封裝 + 行為委托」的設計,將狀態相關的復雜性從業務邏輯中剝離,使系統在面對狀態變化時更具彈性。下一篇我們將深入探討責任鏈模式,解析從 Sentinel 流控到審批流程的鏈式處理邏輯,敬請期待!

擴展思考:狀態模式 vs 策略模式

兩者都通過「封裝變化」實現行為切換,但核心目標不同:

模式變化維度狀態關聯典型應用
狀態模式對象的狀態(動態變化)狀態之間存在依賴和轉換狀態機驅動的業務流程
策略模式算法或策略的選擇(靜態替換)無狀態依賴,策略間獨立不同排序算法、支付方式選擇

理解這種差異,能幫助我們在設計時更精準地選擇合適的模式。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76551.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76551.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76551.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ubuntu 24.02部署java web服務

ubuntu 24.02 版本推薦使用jdk 21版本部署java web服務&#xff0c;開發后先使用sudo java -jar xxx.jar驗證運行結果。 jdk安裝&#xff1a;sudo apt install openjdk-21-jdk-headless 編輯服務文本 [Unit] DescriptionWebMgr Java Application Afternetwork.target mysql.…

深入淺出:LDAP 協議全面解析

在網絡安全和系統管理的世界中&#xff0c;LDAP&#xff08;輕量級目錄訪問協議&#xff0c;Lightweight Directory Access Protocol&#xff09;是一個不可忽視的核心技術。它廣泛應用于身份管理、認證授權以及目錄服務&#xff0c;尤其在企業級環境中占據重要地位。本文將從基…

AI書籍大模型微調-基于亮數據獲取垂直數據集

大模型的開源&#xff0c;使得每位小伙伴都能獲得AI的加持&#xff0c;包括你可以通過AIGC完成工作總結&#xff0c;圖片生成等。這種加持是通用性的&#xff0c;并不會對個人的工作帶來定制的影響&#xff0c;因此各個行業都出現了垂直領域大模型。 垂直大模型是如何訓練出來…

【2025軟考高級架構師】——計算機系統基礎(7)

摘要 本文主要介紹了計算機系統的組成&#xff0c;包括硬件和軟件兩大部分。硬件由處理器、存儲器、總線、接口和外部設備等組成&#xff0c;軟件則涵蓋系統軟件和應用軟件。文章還詳細闡述了馮諾依曼計算機的組成結構&#xff0c;包括 CPU、主存儲器、外存等&#xff0c;并解…

AI大模型之模型幻覺

模型幻覺(Model Hallucination)是大模型生成內容時脫離事實、虛構信息的一種現象,尤其在語言模型、圖像生成模型等中較為常見。為了更深入地理解這一現象,我們可以將其分為兩個維度進行分析:內在幻覺(Intrinsic Hallucination) 和 外在幻覺(Extrinsic Hallucination)。…

spring Ai---向量知識庫(一)

在一些垂直領域以及公司內部信息相關或者實時性相關的大模型應用&#xff0c;就無法直接使用chatGPT。 這個時候&#xff0c;向量知識庫就進入了。 通過坐標向量最接近的即為匹配相關答案。 向量模型定義&#xff1a;將文檔向量化&#xff0c;保證內容越相似的文本&#xff0c;…

Viper配置管理筆記

一、什么是 Viper&#xff1f; Viper 是 Go 語言的一個強大工具&#xff0c;就像一個超級管家&#xff0c;專門負責幫你打理程序的各種配置。它能把配置文件&#xff08;比如 JSON、YAML、TOML 等格式&#xff09;里的內容讀出來&#xff0c;還能監控配置文件的變化&#xff0…

實現對象之間的序列化和反序列化

1.什么是序列化&#xff1f; 在項目的開發中&#xff0c;為了讓前端更好的分析后端返回的結果&#xff0c;我們一般會將返回的信息進行序列化&#xff0c;序列化就是將返回對象的狀態信息轉換為一種標準化的格式&#xff0c;方便在網絡中傳輸也方便打印日志時號觀察&#xff0…

ThreadLocal - 原理與應用場景詳解

ThreadLocal 的基礎概念 在 Java 的多線程世界里&#xff0c;線程之間的數據共享與隔離一直是一個關鍵話題。如果處理不當&#xff0c;很容易引發線程安全問題&#xff0c;比如數據混亂、臟讀等。而 ThreadLocal 這個工具類&#xff0c;就像是為線程量身定制的 “私人儲物柜”…

iwebsec靶場 文件包含關卡通關筆記11-ssh日志文件包含

目錄 日志包含 1.構造惡意ssh登錄命令 2.配置ssh日志開啟 &#xff08;1&#xff09;配置sshd &#xff08;2&#xff09;配置rsyslog &#xff08;3&#xff09;重啟服務 3.寫入webshell木馬 4.獲取php信息滲透 5.蟻劍連接 日志包含 1.構造惡意ssh登錄命令 ssh服務…

Diamond軟件的使用--(4)搭建Modelsim仿真庫

使用Modelsim仿真的原因 由于diamond自帶的仿真軟件Active-HDL需要另一套Lisence&#xff0c;所以我們使用第三方仿真軟件Modelsim來進行仿真。 Modelsim10.5下載鏈接如下&#xff1a;https://pan.baidu.com/s/1G9699ocWm1UTqK2yS2igyQ 提取碼&#xff1a;lewa 一、Lattice仿…

2025年4月19日,四月第三周,C++,字符串數組答案解析

答案與解析 1. 輸出字符串數組所有元素 cpp 復制 下載 #include <iostream> using namespace std;int main() {string arr[] = {"apple", "banana", "cherry"};int n = sizeof(arr)/sizeof(arr[0]); // 計算數組長度for (int i = 0; …

C語言之高校學生信息快速查詢系統的實現

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 總有人間一兩風&#xff0c;填我十萬八千夢。 &#x1f680; 路漫漫其修遠兮&#xff0c;吾將上下而求索。 C語言之高校學生信息快速查詢系統的實現 目錄 任務陳述與分析 問題陳述問題分析 數據結構設…

【網絡篇】TCP vs UDP底層區別+網絡編程概念

大家好呀 我是浪前 今天講解的是網絡篇的第三章&#xff1a;網絡編程概念和TCP&UDP的區別 網絡編程概念TCP和UDP的區別 跨主機通信:網絡編程插座&#xff1a;網絡編程的本質&#xff1a; 網絡編程的重要概念&#xff1a;客戶端和服務器&#xff1a; 客戶端和服務器的交互模…

EMIF詳解

一、EMIF的基本定義 EMIF&#xff08;External Memory Interface&#xff0c;外部存儲器接口&#xff09; 是嵌入式處理器&#xff08;如DSP、FPGA、SoC&#xff09;用于連接外部存儲器的專用硬件接口模塊&#xff0c;負責管理處理器與存儲器之間的地址/數據總線、控制信號及時…

Keil MDK 編譯問題:function “HAL_IncTick“ declared implicitly

問題與處理策略 問題描述 ..\..\User\stm32f1xx_it.c(141): warning: #223-D: function "HAL_IncTick" declared implicitlyHAL_IncTick(); ..\..\User\stm32f1xx_it.c: 1 warning, 0 errors問題原因 在 stm32f1xx_it.c 文件中調用了 HAL_IncTick()&#xff0c;但…

Java Web項目(一)

框架 java web項目總工分為兩部分&#xff1a;客戶端&#xff08;前端&#xff09;和服務端&#xff08;后端&#xff09; 客戶端發起請求&#xff0c;服務端接受請求并進行處理 發起請求的方式&#xff1a;from表單、jQuery ajax from表單 造成全局的變化&#xff0c;在發…

Dify部署內網時遇到的代理問題及解決辦法

大家知道&#xff0c;在公網環境下利用docker安裝dify源碼鏡像比較容易&#xff0c;詳見我之前的文章&#xff0c;基于dify開發agent、workflow等非常方便&#xff0c;本次想著在內部網絡環境下也完成部署&#xff0c;以方便更多的人使用&#xff0c;但在部署到內網環境下&…

多節點監控的docker管理面板Portainer安裝指南:家庭云計算專家

背景 Portainer 是一個輕量級且功能強大的容器管理面板&#xff0c;專為 Docker 和 Kubernetes 環境設計。它通過直觀的 Web 界面簡化了容器的部署、管理和監控&#xff0c;即使是非技術用戶也能輕松上手。Portainer 支持多節點管理&#xff0c;允許用戶從一個中央控制臺管理多…

Linux內核哈希表學習筆記

前沿 近期項目中需要給自定義的驅動增加一個功能存儲相關的數據信息。結合實際業務層面,最終決定采用哈希表的結構來存儲。因為其具備快速查找,插入和刪除。其實現原理通過散列函數映射到指定位置。時間復雜度O(1).而且運算速度也快,很適合處理大量的數據場景。但是其也有一…