摘要
狀態設計模式是一種行為型設計模式,核心在于允許對象在內部狀態改變時改變行為。它通過狀態對象封裝不同行為,使狀態切換靈活清晰。該模式包含環境類、抽象狀態類和具體狀態類等角色,具有避免大量分支判斷、符合單一職責和開閉原則等特點。適用于訂單狀態管理、流程審批等場景,其結構清晰,實現方式多樣,能有效解決狀態切換問題。
1. 狀態設計模式定義
狀態設計模式(State Pattern)是一種行為型設計模式,它的核心思想是:允許對象在其內部狀態改變時改變它的行為,使得看起來就像修改了它的類。狀態模式允許一個對象在其內部狀態發生改變時改變它的行為。這個對象看起來就像修改了它的類一樣。
狀態模式就像一個“帶有狀態的自動售貨機”——投幣、選擇商品、出貨,每個動作在不同狀態下有不同結果。我們通過狀態對象來封裝不同的行為,讓狀態切換變得靈活而清晰。
1.1. 🧩 角色組成:
角色 | 說明 |
| 環境類,持有當前狀態,定義對外接口,委托狀態對象處理行為 |
| 抽象狀態接口,定義所有狀態的行為方法 |
| 具體狀態類,實現不同狀態下的行為邏輯,并負責狀態切換 |
1.2. ? 特點
- 避免了大量
if-else
或switch-case
分支判斷 - 每個狀態封裝一個獨立的行為邏輯,符合單一職責原則
- 狀態切換內聚在狀態對象內部,符合“開閉原則”
1.3. 🧾 示例場景
- 訂單狀態管理(待支付、已支付、已發貨、已完成)
- 流程審批引擎(待審核、審核中、已駁回、已通過)
- 自動售貨機、工作流、任務調度狀態機等
2. 狀態設計模式結構
狀態模式包含如下角色:
- Context: 環境類
- State: 抽象狀態類
- ConcreteState: 具體狀態類
2.1. 狀態設計模式類圖
2.2. 狀態設計模式時序圖
3. 狀態設計模式實現方式
狀態設計模式的實現方式主要依賴對象狀態的封裝和狀態間的轉換控制。以下是標準實現方式及其在 Java(或類似面向對象語言)中的常見實現結構。
3.1. 定義抽象狀態接口(State
)
public interface State {void handle(Context context);
}
3.2. 定義具體狀態類(ConcreteStateA
, ConcreteStateB
)
public class ConcreteStateA implements State {@Overridepublic void handle(Context context) {System.out.println("當前狀態:A,處理邏輯中...切換到狀態B");context.setState(new ConcreteStateB());}
}public class ConcreteStateB implements State {@Overridepublic void handle(Context context) {System.out.println("當前狀態:B,處理邏輯中...切換到狀態A");context.setState(new ConcreteStateA());}
}
3.3. 定義上下文(環境類 Context
)
public class Context {private State state;public Context(State state) {this.state = state;}public void setState(State state) {this.state = state;}public void request() {state.handle(this); // 委托當前狀態處理}
}
3.4. 客戶端調用示例
public class Main {public static void main(String[] args) {Context context = new Context(new ConcreteStateA());context.request(); // 當前狀態Acontext.request(); // 切換到Bcontext.request(); // 再切換到A}
}
3.5. 狀態設計模式在Spring 或企業項目中的實現方式拓展
在實際項目中,狀態模式常結合如下技術使用:
技術棧/機制 | 實現方式說明 |
Spring 容器管理狀態類 | 使用注解 |
狀態與事件觸發分離 | 可結合策略模式或狀態機框架(如 Spring Statemachine)來支持更復雜的狀態轉移圖 |
結合枚舉做狀態標識 | 使用枚舉表示狀態常量,再映射到狀態實現類,便于狀態持久化與切換 |
結合數據庫存儲狀態值 | 在業務實體中存儲當前狀態字段,狀態類根據字段值決定是否允許轉移 |
3.6. Java 項目中使用狀態模式的一些變體實現方式
實現方式 | 說明 |
經典接口實現 | 每個狀態一個類,符合設計原則,但類多 |
枚舉實現狀態 | 用 |
策略+狀態融合 | 每個狀態類封裝為一個策略行為,通過上下文統一調度 |
注解驅動(配合 AOP) | 某些行為切換狀態可用注解標注事件,然后由切面完成狀態更新 |
總結:狀態模式通過將“行為”委托給“狀態類”,并在狀態類內部控制狀態轉移,既解耦了狀態判斷邏輯,也增強了可擴展性和靈活性。
4. 狀態設計模式適合場景
4.1. ? 適合使用狀態模式的場景
場景 | 說明 |
對象狀態經常變化 | 如訂單、任務、審批流等有多個明確狀態,且狀態切換頻繁、規則復雜。 |
狀態行為復雜且相互不同 | 各狀態對應的行為差異大,不適合用 if/else 處理。例如支付狀態下不能發貨,發貨狀態下不能退款。 |
狀態切換有明確流程或圖譜 | 如狀態轉移圖能清晰表示狀態之間的合法路徑,適合模型驅動開發。 |
希望將狀態行為局部化 | 避免大量 if-else/switch,在每個狀態類中封裝對應邏輯,提高代碼清晰度。 |
可擴展性要求高 | 新增狀態時只需添加一個類,符合開閉原則,不需改動原有邏輯。 |
工作流引擎/狀態機系統開發 | 狀態流轉是核心功能,狀態模式天然適配這類系統。 |
4.2. ? 不適合使用狀態模式的場景
場景 | 原因 |
狀態數量很少,邏輯簡單 | 如只有 2-3 個狀態、行為簡單,使用狀態類反而增加復雜度。 |
狀態行為一致,僅數據不同 | 行為一致可以用策略模式或配置表來處理,無需狀態類區分。 |
狀態轉移規則頻繁變化或不穩定 | 狀態圖不穩定會導致大量狀態類頻繁調整,維護成本高。 |
只需要一個條件判斷即可處理的邏輯 | 強行拆成多個狀態類會讓代碼變啰嗦、不易維護。 |
資源受限的場景(嵌入式、移動端) | 每個狀態一個類可能會增加內存開銷,不如用狀態碼枚舉和 switch 實現更輕量。 |
4.3. 🧠 總結:
如果你面對的是有限狀態機問題(FSM),狀態之間行為差異大且需頻繁切換,那狀態設計模式就是非常合適的選擇;反之,則應避免“為了模式而模式”。
5. 狀態設計模式實戰示例
5.1. ? 場景說明:
模擬一個信貸風控審批流程,審批任務的狀態有以下幾種:
待初審
→初審通過
→復審通過
→審批完成
- 各個狀態下行為不同,如“提交”、“退回”、“終止”等
5.2. ? 類結構圖(State 模式組成)
[State] ← 抽象接口↑ ↑ ↑
[AState] [BState] [CState] ← 具體狀態類[ApprovalContext] ← 上下文類,包含狀態對象 + 狀態切換邏輯
5.3. 🛠 抽象狀態接口
public interface ApprovalState {void submit(ApprovalContext context);void reject(ApprovalContext context);String getStateCode(); // 標識狀態
}
5.4. 🛠 上下文類(使用 Spring 注解注入狀態對象)
@Component
public class ApprovalContext {// 所有狀態實現類注入到 Map,key 為狀態 code@Autowiredprivate List<ApprovalState> stateList;private Map<String, ApprovalState> stateMap;private ApprovalState currentState;@PostConstructpublic void init() {stateMap = stateList.stream().collect(Collectors.toMap(ApprovalState::getStateCode, s -> s));}public void setCurrentState(String stateCode) {this.currentState = stateMap.get(stateCode);}public void submit() {currentState.submit(this);}public void reject() {currentState.reject(this);}public String getCurrentStateCode() {return currentState.getStateCode();}
}
5.5. 🛠 具體狀態實現類(以“待初審”為例)
@Component
public class WaitInitialApprovalState implements ApprovalState {@Overridepublic void submit(ApprovalContext context) {System.out.println("當前狀態:待初審 → 執行提交 → 切換到初審通過");context.setCurrentState("APPROVED_INIT");}@Overridepublic void reject(ApprovalContext context) {System.out.println("當前狀態:待初審 → 執行駁回 → 切換到終止狀態");context.setCurrentState("TERMINATED");}@Overridepublic String getStateCode() {return "WAIT_INIT";}
}
再如 “初審通過” 狀態:
@Component
public class ApprovedInitialState implements ApprovalState {@Overridepublic void submit(ApprovalContext context) {System.out.println("當前狀態:初審通過 → 執行提交 → 切換到復審通過");context.setCurrentState("APPROVED_FINAL");}@Overridepublic void reject(ApprovalContext context) {System.out.println("當前狀態:初審通過 → 駁回 → 回到初審");context.setCurrentState("WAIT_INIT");}@Overridepublic String getStateCode() {return "APPROVED_INIT";}
}
5.6. 🧪 控制層模擬調用
@RestController
@RequestMapping("/approval")
public class ApprovalController {@Autowiredprivate ApprovalContext context;@GetMapping("/start")public String start() {context.setCurrentState("WAIT_INIT");return "流程已啟動,當前狀態:" + context.getCurrentStateCode();}@PostMapping("/submit")public String submit() {context.submit();return "提交后,當前狀態:" + context.getCurrentStateCode();}@PostMapping("/reject")public String reject() {context.reject();return "駁回后,當前狀態:" + context.getCurrentStateCode();}
}
5.7. 🧠 技術亮點
點 | 說明 |
? Spring 注解注入 | 使用 |
? Map 管理狀態 | 使用 |
? 避免構造注入 | 通過字段注入 |
? 易于擴展 | 添加新狀態只需新增一個實現類,無需修改原有邏輯,符合開閉原則 |
5.8. ? 總結:
本示例將狀態設計模式與 Spring 注解機制結合,實現了一個靈活、可擴展的金融風控審批流程狀態機,適合真實風控系統中審批流、處理流的狀態管理需求。
6. 狀態設計模式思考
6.1. 狀態設計模式與狀態機設計有什么關系?
狀態設計模式(State Pattern)與狀態機(State Machine)有密切關系,但兩者側重點不同。理解它們的聯系與區別有助于你在項目中正確選擇使用方式,特別是在風控、審批流等系統中。
6.1.1. ? 二者關系概述:
項目 | 狀態設計模式(State Pattern) | 狀態機(State Machine) |
核心概念 | 將狀態行為封裝為類,行為由當前狀態對象決定 | 明確的狀態集合、事件集合、轉移規則 |
關注點 | 封裝狀態行為和狀態切換邏輯(面向對象) | 管理狀態 + 事件 + 轉移圖譜(面向模型) |
表達能力 | 表達某對象在不同狀態下行為不同 | 能描述復雜的狀態流、事件觸發與狀態轉移 |
實現方式 | 使用狀態類、上下文對象,代碼驅動 | 可以是代碼、配置、狀態轉移圖、框架等 |
適合場景 | 狀態數有限,行為差異大,關注行為封裝 | 狀態眾多,轉移復雜,關注狀態流和路徑 |
是否一定使用類 | ? 每個狀態類實現接口 | ? 可純配置(如狀態轉移表、DSL) |
常見代表 | 狀態模式(GoF) | 有限狀態機(FSM)、Spring Statemachine |
6.1.2. ? 類比舉例:審批流程
- 狀態設計模式:
你創建WaitApprovalState
、ApprovedState
、RejectedState
等類,在類中定義“提交”、“駁回”等行為。 - 狀態機模型:
你建一個狀態圖:
WAIT_APPROVAL
--(submit)-->APPROVED
WAIT_APPROVAL
--(reject)-->REJECTED
并用框架(如 Spring Statemachine)實現。
6.1.3. ? 狀態設計模式 vs 狀態機的總結圖解:
┌─────────────────────┐│ 狀態設計模式 ││ 封裝狀態行為 ││ 各狀態一個類 │└────────┬────────────┘│▼狀態邏輯復雜,可擴展性強│▼┌─────────────────────┐│ 狀態機模型 ││ 管理狀態轉移路徑 ││ 可視化或配置驅動 │└─────────────────────┘
6.2. ? 實踐建議
情況 | 推薦方案 |
狀態較少、行為差異大 | 使用狀態設計模式,可讀性強、便于擴展 |
狀態較多、轉移復雜、事件驅動 | 使用狀態機模型(框架),如 Spring Statemachine |
想表達清晰狀態流 | 先畫出狀態圖,用狀態機模型來支撐業務流程設計 |
6.3. 🧠 總結:
狀態設計模式是狀態機的一種實現方式,適用于行為封裝;而狀態機更偏向建模工具,適用于流程表達和自動化管理。兩者可以結合使用,如狀態類 + 狀態圖配置來實現靈活狀態系統。
博文參考
- 4. 狀態模式 — Graphic Design Patterns
- 狀態設計模式
- 設計模式之狀態模式 | DESIGN