設計模式(二十一)行為型:狀態模式詳解

設計模式(二十一)行為型:狀態模式詳解

狀態模式(State Pattern)是 GoF 23 種設計模式中的行為型模式之一,其核心價值在于允許一個對象在其內部狀態改變時改變其行為,使得對象看起來像是修改了它的類。它通過將與特定狀態相關的行為封裝到獨立的狀態類中,將龐大的條件分支(如 if-elseswitch-case)轉化為對象間的委托關系,從而實現行為的動態切換與高度可擴展性。狀態模式是構建復雜狀態機、工作流引擎、游戲角色行為、網絡連接管理、訂單生命周期、UI 狀態管理等系統的理想選擇,是將“狀態驅動行為”這一自然邏輯優雅映射到面向對象設計的關鍵范式。

一、詳細介紹

狀態模式解決的是“一個對象的行為取決于其內部狀態,且狀態數量較多、狀態轉換復雜、行為隨狀態頻繁變化”的問題。在傳統設計中,通常使用條件語句(如 switch)根據當前狀態決定執行何種行為。這導致:

  • 代碼臃腫:所有狀態相關的行為集中在單一類中,方法龐大。
  • 難以維護:新增狀態或修改狀態轉換邏輯需要修改大量 switch 語句。
  • 違反單一職責原則:一個類承擔了所有狀態的行為。
  • 違反開閉原則:對擴展開放,對修改關閉。

狀態模式的核心思想是:將每個狀態封裝成一個獨立的類,每個狀態類實現與該狀態相關的行為。原對象(上下文)持有當前狀態對象的引用,并將狀態相關的行為委托給當前狀態對象執行

該模式包含以下核心角色:

  • Context(上下文):定義客戶端使用的接口,維護一個對 State 對象的引用,表示當前狀態。它將狀態相關的行為委托給當前狀態對象。
  • State(狀態接口):定義所有具體狀態類共有的接口,聲明與狀態相關的行為方法(如 handle())。
  • ConcreteStateA, ConcreteStateB, …(具體狀態類):實現 State 接口,封裝與特定狀態相關的行為。每個具體狀態類知道在特定操作下應如何響應,并可能在響應后改變上下文的當前狀態(即狀態轉換)。

狀態模式的關鍵優勢:

  • 消除復雜條件語句:將 switch 邏輯轉化為多態方法調用。
  • 符合開閉原則:新增狀態只需添加新的具體狀態類,無需修改現有代碼。
  • 符合單一職責原則:每個狀態類只負責一種狀態的行為。
  • 提高可維護性:狀態行為集中,邏輯清晰。
  • 支持動態狀態轉換:狀態轉換由狀態對象內部決定或由上下文協調。

與“策略模式”相比,策略模式關注算法的替換,狀態模式關注狀態驅動的行為變化;策略通常由客戶端選擇,狀態由內部邏輯自動轉換。與“觀察者模式”相比,觀察者是一對多通知,狀態是單對象行為切換。與“命令模式”相比,命令封裝請求,狀態封裝行為

狀態模式適用于:

  • 對象有明確的狀態概念(如訂單:新建、已支付、已發貨、已完成)。
  • 行為隨狀態變化而變化。
  • 狀態轉換邏輯復雜。
  • 需要避免龐大的條件語句。

二、狀態模式的UML表示

以下是狀態模式的標準 UML 類圖:

has current
implements
implements
implements
Context
-state: State
+request()
+setState(state: State)
+getState()
?interface?
State
+handle(context: Context)
ConcreteStateA
+handle(context: Context)
ConcreteStateB
+handle(context: Context)
ConcreteStateC
+handle(context: Context)

圖解說明

  • Context 持有對 State 的引用,代表當前狀態。
  • Contextrequest() 方法將調用委托給當前 Statehandle()
  • ConcreteState 實現 handle(),執行特定于該狀態的行為,并可能調用 ContextsetState() 來觸發狀態轉換。
  • 狀態轉換可以由狀態對象自身決定,或由 Context 根據業務邏輯協調。

三、一個簡單的Java程序實例及其UML圖

以下是一個 TCP 連接狀態機的簡化示例,包含 CLOSED, LISTEN, ESTABLISHED, CLOSE_WAIT 狀態。

Java 程序實例
// 狀態接口
interface TCPState {void activeOpen(TCPConnection context);void passiveOpen(TCPConnection context);void close(TCPConnection context);void acknowledge(TCPConnection context);void send(TCPConnection context);String getStateName(); // 用于顯示
}// 上下文:TCP連接
class TCPConnection {private TCPState currentState;// 預定義狀態實例(可單例)private static final TCPState CLOSED = new ClosedState();private static final TCPState LISTEN = new ListenState();private static final TCPState ESTABLISHED = new EstablishedState();private static final TCPState CLOSE_WAIT = new CloseWaitState();public TCPConnection() {this.currentState = CLOSED;System.out.println("🔌 連接初始化為 CLOSED 狀態");}// 狀態相關操作,委托給當前狀態public void activeOpen() {System.out.println("👉 客戶端發起主動打開...");currentState.activeOpen(this);}public void passiveOpen() {System.out.println("👉 服務端發起被動打開...");currentState.passiveOpen(this);}public void close() {System.out.println("👉 發起關閉連接...");currentState.close(this);}public void acknowledge() {System.out.println("👉 收到確認...");currentState.acknowledge(this);}public void send() {System.out.println("👉 發送數據...");currentState.send(this);}// 狀態轉換方法public void changeState(TCPState newState) {if (this.currentState != null) {System.out.println("🔄 狀態轉換: " + this.currentState.getStateName() + " → " + newState.getStateName());}this.currentState = newState;}// 獲取當前狀態(用于狀態判斷,可選)public TCPState getCurrentState() {return currentState;}// 預定義狀態的獲取方法(簡化客戶端使用)public static TCPState getClosedState() { return CLOSED; }public static TCPState getListenState() { return LISTEN; }public static TCPState getEstablishedState() { return ESTABLISHED; }public static TCPState getCloseWaitState() { return CLOSE_WAIT; }
}// 具體狀態:CLOSED
class ClosedState implements TCPState {@Overridepublic void activeOpen(TCPConnection context) {System.out.println("  ? CLOSED: 執行主動打開 -> 發送 SYN, 進入 SYN_SENT (本例簡化為直接進入 ESTABLISHED)");context.changeState(TCPConnection.getEstablishedState());}@Overridepublic void passiveOpen(TCPConnection context) {System.out.println("  ? CLOSED: 執行被動打開 -> 進入 LISTEN");context.changeState(TCPConnection.getListenState());}@Overridepublic void close(TCPConnection context) {System.out.println("  ??  CLOSED: 連接已關閉,無需操作");}@Overridepublic void acknowledge(TCPConnection context) {System.out.println("  ? CLOSED: 無法處理確認");}@Overridepublic void send(TCPConnection context) {System.out.println("  ? CLOSED: 連接未建立,無法發送");}@Overridepublic String getStateName() {return "CLOSED";}
}// 具體狀態:LISTEN
class ListenState implements TCPState {@Overridepublic void activeOpen(TCPConnection context) {System.out.println("  ? LISTEN: 收到 SYN -> 發送 SYN-ACK, 進入 SYN_RECEIVED (本例簡化為進入 ESTABLISHED)");context.changeState(TCPConnection.getEstablishedState());}@Overridepublic void passiveOpen(TCPConnection context) {System.out.println("  ??  LISTEN: 已在監聽狀態");}@Overridepublic void close(TCPConnection context) {System.out.println("  ? LISTEN: 關閉監聽 -> 進入 CLOSED");context.changeState(TCPConnection.getClosedState());}@Overridepublic void acknowledge(TCPConnection context) {System.out.println("  ? LISTEN: 未建立連接,無法確認");}@Overridepublic void send(TCPConnection context) {System.out.println("  ? LISTEN: 連接未建立,無法發送");}@Overridepublic String getStateName() {return "LISTEN";}
}// 具體狀態:ESTABLISHED
class EstablishedState implements TCPState {@Overridepublic void activeOpen(TCPConnection context) {System.out.println("  ??  ESTABLISHED: 連接已建立,無需打開");}@Overridepublic void passiveOpen(TCPConnection context) {System.out.println("  ??  ESTABLISHED: 連接已建立,無需打開");}@Overridepublic void close(TCPConnection context) {System.out.println("  ? ESTABLISHED: 發送 FIN -> 進入 FIN_WAIT_1 (本例簡化為進入 CLOSE_WAIT)");context.changeState(TCPConnection.getCloseWaitState());}@Overridepublic void acknowledge(TCPConnection context) {System.out.println("  ? ESTABLISHED: 確認數據包");// 通常發送 ACK}@Overridepublic void send(TCPConnection context) {System.out.println("  ? ESTABLISHED: 數據發送成功");// 發送數據包}@Overridepublic String getStateName() {return "ESTABLISHED";}
}// 具體狀態:CLOSE_WAIT
class CloseWaitState implements TCPState {@Overridepublic void activeOpen(TCPConnection context) {System.out.println("  ? CLOSE_WAIT: 連接正在關閉,無法打開");}@Overridepublic void passiveOpen(TCPConnection context) {System.out.println("  ? CLOSE_WAIT: 連接正在關閉,無法打開");}@Overridepublic void close(TCPConnection context) {System.out.println("  ? CLOSE_WAIT: 發送 FIN -> 進入 LAST_ACK (本例簡化為進入 CLOSED)");context.changeState(TCPConnection.getClosedState());}@Overridepublic void acknowledge(TCPConnection context) {System.out.println("  ? CLOSE_WAIT: 確認對方的 FIN");}@Overridepublic void send(TCPConnection context) {System.out.println("  ??  CLOSE_WAIT: 可能允許發送最后數據,但本例禁止");}@Overridepublic String getStateName() {return "CLOSE_WAIT";}
}// 客戶端使用示例
public class StatePatternDemo {public static void main(String[] args) {System.out.println("🌐 TCP 連接狀態機 - 狀態模式示例\n");TCPConnection connection = new TCPConnection();System.out.println("\n--- 客戶端連接流程 ---");connection.activeOpen(); // CLOSED -> ESTABLISHEDconnection.send();       // 發送數據connection.acknowledge(); // 確認connection.close();      // ESTABLISHED -> CLOSE_WAIT -> CLOSEDSystem.out.println("\n--- 服務端連接流程 ---");connection.passiveOpen(); // CLOSED -> LISTENconnection.activeOpen();  // LISTEN -> ESTABLISHED (收到客戶端 SYN)connection.send();        // 發送數據connection.close();       // ESTABLISHED -> CLOSE_WAIT -> CLOSEDSystem.out.println("\n--- 在 CLOSED 狀態嘗試無效操作 ---");connection.send();        // 應提示無法發送}
}
實例對應的UML圖(簡化版)
has current
implements
implements
implements
implements
TCPConnection
-currentState: TCPState
+activeOpen()
+passiveOpen()
+close()
+acknowledge()
+send()
+changeState(newState: TCPState)
?interface?
TCPState
+activeOpen(context: TCPConnection)
+passiveOpen(context: TCPConnection)
+close(context: TCPConnection)
+acknowledge(context: TCPConnection)
+send(context: TCPConnection)
+getStateName()
ClosedState
+activeOpen(context: TCPConnection)
+passiveOpen(context: TCPConnection)
+close(context: TCPConnection)
+acknowledge(context: TCPConnection)
+send(context: TCPConnection)
+getStateName()
ListenState
+activeOpen(context: TCPConnection)
+passiveOpen(context: TCPConnection)
+close(context: TCPConnection)
+acknowledge(context: TCPConnection)
+send(context: TCPConnection)
+getStateName()
EstablishedState
+activeOpen(context: TCPConnection)
+passiveOpen(context: TCPConnection)
+close(context: TCPConnection)
+acknowledge(context: TCPConnection)
+send(context: TCPConnection)
+getStateName()
CloseWaitState
+activeOpen(context: TCPConnection)
+passiveOpen(context: TCPConnection)
+close(context: TCPConnection)
+acknowledge(context: TCPConnection)
+send(context: TCPConnection)
+getStateName()

運行說明

  • TCPConnection 是上下文,持有當前 TCPState
  • 每個操作(activeOpen, close 等)被委托給當前狀態對象的對應方法。
  • 狀態方法執行特定行為,并可能調用 changeState() 觸發狀態轉換。
  • 不同狀態對同一操作的響應不同(如 CLOSEDESTABLISHEDsend 的處理)。
  • 狀態轉換邏輯內置于狀態類中,清晰且可擴展。

四、總結

特性說明
核心目的允許對象在狀態改變時改變行為,消除條件分支
實現機制將狀態行為封裝到獨立類,上下文委托調用
優點消除復雜條件語句、符合開閉/單一職責原則、提高可維護性、支持動態轉換
缺點增加類數量、狀態轉換邏輯可能分散、簡單狀態機可能過度設計
適用場景復雜狀態機、工作流、訂單生命周期、游戲AI、UI狀態管理
不適用場景狀態極少、行為簡單、狀態轉換固定

狀態模式使用建議

  • 狀態類可設計為單例(無狀態或共享狀態)。
  • 狀態轉換可由狀態對象自身決定,或由上下文根據業務規則協調。
  • 可結合“工廠模式”創建狀態對象。
  • 在 Java 中,enum 可實現簡單狀態模式(每個枚舉常量實現接口)。

架構師洞見:
狀態模式是“有限狀態機(FSM)”在面向對象中的優雅實現。在現代架構中,其思想已演變為工作流引擎(如 Camunda, Airflow)事件驅動狀態管理(Redux, Vuex)服務編排(Kubernetes Operators)AI Agent 的決策狀態 的核心。例如,Redux 的 reducer 函數根據 action 和當前 state 計算新 state,本質是狀態模式的函數式變體;微服務的 Saga 模式管理分布式事務狀態;在自動駕駛中,車輛行為模式(巡航、變道、停車)是復雜的狀態機。

未來趨勢是:狀態模式將與形式化方法結合,實現狀態機的自動驗證;在量子計算中,量子態的演化可建模為狀態轉換;在元宇宙中,虛擬角色的行為狀態(行走、戰斗、交互)由狀態模式驅動;在AI中,LLM Agent 的“思考-行動-觀察”循環可視為一個高級狀態機。

掌握狀態模式,是設計復雜業務邏輯、高可靠性系統的關鍵。作為架構師,應在面對“多狀態、多行為、復雜轉換”的領域模型時,果斷采用狀態模式。它不僅是模式,更是系統確定性的保障——它將混沌的條件邏輯轉化為清晰、可驗證、可演進的狀態圖,讓系統的每一次行為變遷都變得可預測、可追溯、可管理,從而構建出真正健壯、可信賴的軟件系統。

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

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

相關文章

深入理解 Doris Compaction:提升查詢性能的幕后功臣

在 Doris 的數據存儲與查詢體系里,Compaction 是保障查詢效率、優化存儲結構的關鍵機制。如果你好奇 Doris 如何在高頻寫入后仍能高效響應查詢,或是想解決數據版本膨脹帶來的性能問題,這篇關于 Compaction 的深度解析值得收藏 👇 …

css 實現虛線效果的多種方式

使用邊框實現虛線 通過設置元素的邊框樣式來實現虛線效果。以下為示例代碼: .dashed {border: 1px dashed black; }使用 CSS 偽元素實現虛線 使用偽元素來模擬虛線的效果。以下為示例代碼: .dashed::before {content: "";display: block;height: 1px;border-bo…

深入剖析 RocketMQ 分布式事務:原理、流程與實踐

Apache RocketMQ 是一種分布式消息隊列系統,支持分布式事務消息,以確保在分布式系統中數據的一致性。它通過一種基于兩階段提交(2PC)的機制結合補償邏輯來實現分布式事務的最終一致性。以下是對 RocketMQ 分布式事務的詳細講解,包括其核心概念…

具身智能 自動駕駛相關崗位的技術棧與能力地圖

一、硬技能技術棧(優先級排序) 1. 核心領域技術(★★★★★)技術方向具體技能學習建議大模型實戰- VLA架構(RT-2、PaLM-E)開發/微調- 多模態對齊(CLIP、Flamingo)- 生成式策略&#…

實現了加載 正向 碰撞 雅可比 仿真

""" # 此示例從 URDF 文件中加載一個 UR10 機械臂的模型 # 隨后演示 Pinocchio 庫的基本功能,如正向運動學計算 # 雅可比矩陣計算、碰撞檢測以及動力學仿真 """ # 導入 meshcat 的幾何模塊,用于創建和管理可視化的幾何對象 import meshcat.geo…

【0基礎PS】PS工具詳解--畫筆工具

目錄前言一、畫筆工具的位置與快捷鍵?二、畫筆工具選項欄設置?三、畫筆工具的進階應用?四、常見問題及解決方法?總結前言 在 Photoshop 的眾多工具中,畫筆工具無疑是極具創造力和實用性的工具之一。無論是進行圖像繪制、照片修飾,還是特效制作&…

window10和ubuntu22.04雙系統之卸載ubuntu系統

window10和ubuntu22.04雙系統之卸載ubuntu系統)1. 刪除Ubuntu系統占用的磁盤分區(在Windows下操作)2. 刪除ubuntu開機引導項1. winr出來終端提示框后輸入2. 然后會在命令行中顯示電腦的硬盤列表,輸入命令選擇安裝Windows的那個硬盤…

(C++)C++類和類的方法(基礎教程)(與Python類的區別)

前言&#xff1a; 本篇博客建議搭配&#xff1a;&#xff08;Python&#xff09;類和類的方法&#xff08;基礎教程介紹&#xff09;&#xff08;Python基礎教程&#xff09;-CSDN博客 一起學習使用&#xff1b; 源代碼&#xff1a; #include <iostream> #include &…

【NLP輿情分析】基于python微博輿情分析可視化系統(flask+pandas+echarts) 視頻教程 - 微博文章數據可視化分析-文章分類下拉框實現

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;最近寫了一套【NLP輿情分析】基于python微博輿情分析可視化系統(flaskpandasecharts)視頻教程&#xff0c;持續更新中&#xff0c;計劃月底更新完&#xff0c;感謝支持。今天講解微博文章數據可視化分析-文章分類下拉框實現…

Git命令保姆級教程

Git 入門網站 https://learngitbranching.js.org/?localezh_CN Git 命令 git init // 在本地目錄內部會生成.git文件夾 git initgit clone // 從git服務器拉取代碼 // 代碼下載完成后在當前文件夾中會有一個 shop 的目錄&#xff0c;通過 cd shop 命令進入目錄。 git clone ht…

Java Ai For循環 (day07)

循環結構 for&#xff1a;循環語句的作用&#xff1a;可以將一段代碼重復的執行很多次for 循環語句格式&#xff1a;執行流程&#xff1a; 初始化語句執行條件判斷語句&#xff0c;看結果是 true&#xff0c;還是 false false結束&#xff0c;true繼續執行循環體語句執行條件控…

Directory Opus 使用優化

自定義快捷鍵 Directory Opus 移動標簽到另一欄 設置快捷鍵&#xff1a;ctrl←/→ 設置步驟&#xff1a; 打開【設置】—>選擇【自定義工具欄和快捷鍵】 選擇【新建】—>【新建窗口快捷鍵】 輸入快捷鍵命令 Go TABMOVEother此時可以點擊運行進行測試&#xff0c;…

Qt知識點2『Ubuntu24.04.2安裝Qt5.12.9各種報錯』

問題1&#xff1a;Qt安裝完畢后&#xff0c;新建一個最簡單的測試程序&#xff0c;但是QtCreator左側構建的三個按鈕呈現灰色&#xff0c;無法進行構建操作答&#xff1a;進入QtCreator的Kits界面&#xff08;工具-選項&#xff09;&#xff0c;點擊"自動檢測"下的De…

TS面試題

1.TS有哪些類型&#xff08;對比與js&#xff09;&#xff1f;關鍵字/語法用途示例any關閉類型檢查let a: any 4unknown類型安全的 anylet u: unknown 4; if (typeof u number) …never永不存在的值function err(): never { throw 0; }void無返回值function f(): void {}enu…

借助Early Hints和HarperDB改善網頁性能

對電商網站來說&#xff0c;糟糕的頁面性能可能會增加交易放棄率。一直以來&#xff0c;人們會使用CDN進行緩存從而縮短頁面加載時間&#xff0c;但即便實施了強大的緩存&#xff0c;消費者在通過移動網絡訪問這些網站時可能仍然會需要頻繁等待。最近誕生了一種名為“早期提示”…

MEMS陀螺如何成為無人機穩定飛行的核心?

在無人機自主翱翔、靈活機動并適應多變環境的背后&#xff0c;對其運動狀態——尤其是姿態——的精確感知是基石。作為飛行控制系統&#xff08;飛控&#xff09;的“內耳”&#xff0c;陀螺儀實時捕捉機體繞X、Y、Z三軸的旋轉角速度。這一核心數據是飛控進行姿態解算和維持飛行…

騰訊云拉取docker鏡像失敗怎么辦

ps:我直接按照步驟1和2就解決了 以下內容來自豆包 在騰訊云服務器上拉取 Docker 鏡像失敗&#xff0c;可以按照以下步驟排查和解決&#xff1a; 一、檢查網絡連接 確認服務器網絡正常 bash ping www.baidu.com # 測試公網連通性如果無法 ping 通&#xff0c;檢查服務器防火墻…

Apache FOP實踐——pdf模板引擎

文章目錄 基本概念設計思想具體實踐完整應用 基本概念 Apache FOP&#xff08;Formatting Objects Processor&#xff09;是一個基于Java的開源工具&#xff0c;用于將 XSL-FO&#xff08;XSL Formatting Objects&#xff09; 文檔轉換為PDF、圖像等格式。 設計思想 將內容&…

WebRTC核心組件技術解析:架構、作用與協同機制

引言&#xff1a;WebRTC的技術定位與價值 WebRTC&#xff08;Web Real-Time Communication&#xff09;作為一項開源實時通信標準&#xff0c;已成為瀏覽器原生音視頻交互、P2P數據傳輸的技術基石。自2011年開源以來&#xff0c;其標準化進程由W3C&#xff08;API層&#xff0…

OmniParser:提升工作效率的視覺界面解析工具

OmniParser&#xff1a;基于視覺的用戶界面解析工具在現代軟件開發中&#xff0c;用戶界面的自動化處理變得愈發重要。OmniParser 是一個強大的工具&#xff0c;旨在將用戶界面的截圖解析為結構化的、易于理解的元素&#xff0c;從而顯著提升了大型語言模型&#xff08;如GPT-4…