設計模式之狀態模式
實際開發中訂單往往都包含着訂單狀態,用户每進行一次操作都要切換對應的狀態,而每次切換判斷當前的狀態是必須的,就不可避免的引入一系列判斷語句,為了讓代碼更加清晰直觀,我們引入今天的主角——狀態模式。
一、概念理解
假設訂單狀態有,下單、發貨、確認收貨,如果用户確認收貨,在常規編程中就要判斷當前用户的狀態,然後再修改狀態,如果這種情況下使用狀態模式。
將各個狀態都抽象成一個狀態類,比如下單狀態類、發貨狀態類、確認收貨類,在狀態類中處理相應的邏輯和控制下一個狀態,在定義一個環境類,定義初始狀態,並控制切換狀態。
在狀態模式中應該包含着三個角色:
環境類(Context)角色:也稱為上下文,它定義了客户端需要的接口,內部維護一個當前狀態,這個類持有State接口,負責保持並切換當前的狀態。
抽象狀態(State)角色:定義一個接口,用以封裝環境對象中的特定狀態所對應的行為,可以有一個或多個行為。
具體狀態(Concrete State)角色:實現抽象狀態所對應的行為,並且在需要的情況下進行狀態切換。
以下為狀態模式的類圖,看起來是很直觀的,理解起來也簡單,我們需要説明的是狀態模式的類圖和策略模式的類圖長的一樣,但寫起來狀態模式比策略模式要難。
我們要注意這段話,在狀態模式中,類的行為是基於它的狀態改變的,狀態之間的切換,在狀態A執行完畢後自己控制狀態指向狀態B,狀態模式是不停的切換狀態執行。這也是狀態模式和策略模式不一樣的地方。
另外在狀態模式中,狀態A到B是由自己控制的,而不是由客户端來控制,這是狀態模式和策略模式最顯著的特徵。
我們基於訂單狀態案例實現demo。
二、案例實現
抽象狀態:
定義統一的狀態切換方法
/** * 抽象狀態 * @author tcy * @Date 20-09-2022 */ public abstract class OrderStateAbstract { protected Context context; public void setContext(Context context) { this.context = context; } /** * 狀態切換 */ public abstract void handle(); }
具體狀態-訂單付款:
實現狀態接口,處理相應的邏輯,並定義下一個狀態
/** * 訂單付款 * @author tcy * @Date 21-09-2022 */ public class OrderStatePay extends OrderStateAbstract { @Override public void handle() { System.out.println("訂單已支付,執行下個狀態..."); context.changeState(new OrderStateOut()); } }
具體狀態-訂單發貨
/** * 訂單發貨 * @author tcy * @Date 21-09-2022 */ public class OrderStateOut extends OrderStateAbstract { @Override public void handle() { System.out.println("訂單已經發貨,開始下一狀態..."); context.changeState(new OrderStateSubmit()); } }
具體狀態-訂單確認收貨
/** * 訂單提交 * @author tcy * @Date 21-09-2022 */ public class OrderStateSubmit extends OrderStateAbstract { @Override public void handle() { System.out.println("訂單已經確認收貨..."); } }
環境類:
持有最新狀態,並調用具體的狀態切換方法
/** * 環境類 * @author tcy * @Date 20-09-2022 */ public class Context { private OrderStateAbstract state; //定義環境類的初始狀態 public Context() { this.state = new OrderStatePay(); state.setContext(this); } //狀態切換 public void changeState(OrderStateAbstract state) { this.state = state; this.state.setContext(this); } /** * 審批通過請求 */ public void request() { this.state.handle(); } }
客户端調用:
/** * @author tcy * @Date 20-09-2022 */ public class Client { public static void main(String[] args) { //創建環境 Context context = new Context(); //訂單付款 context.request(); //訂單發貨 context.request(); //訂單付款 context.request(); } }
狀態模式客户端調用比較簡單,由狀態內部類進行狀態切換。
三、總結
很多博客都將策略模式的案例代碼當做狀態模式來講解,這是不正確的,讀者可以參考 策略模式 兩篇做對比學習,認真體會他們之間的區別。
在實際開發中,當控制一個對象狀態轉換的條件表達式過於複雜時,就可以使用狀態模式把相關“判斷邏輯”提取出來,用各個不同的類進行表示。
系統處於哪種情況,直接使用相應的狀態類對象進行處理,這樣能把原來複雜的邏輯判斷簡單化,消除了 if-else、switch-case 等宂餘語句,代碼更有層次性,並且具備良好的擴展力。
比如審批流程,我們案例也僅僅是用於訂單流程做例子,在實際開發中並不會使用這種方式處理訂單,因為訂單的處理邏輯實際上並不是那麼複雜,引入狀態模式反而增加了更多的類,造成系統更加的複雜,這也是設計模式最顯著的缺點。
設計模式的學習要成體系,推薦你看我往期發佈的設計模式文章。
- 設計模式之狀態模式
- 如何實現數據庫讀一致性
- 我是怎麼入行做風控的
- C 11精要:部分語言特性
- 吳恩達來信:人工智能領域的求職小 tips
- EasyCV帶你復現更好更快的自監督算法-FastConvMAE
- 某車聯網App 通訊協議加密分析(四) Trace Code
- 帶你瞭解CANN的目標檢測與識別一站式方案
- EasyNLP玩轉文本摘要(新聞標題)生成
- PostgreSQL邏輯複製解密
- 基於 CoreDNS 和 K8s 構建雲原生場景下的企業級 DNS
- 循環神經網絡(RNN)可是在語音識別、自然語言處理等其他領域中引起了變革!
- 技術分享| 分佈式系統中服務註冊發現組件的原理及比較
- 利用谷歌地圖採集外貿客户的電話和手機號碼
- 跟我學Python圖像處理丨關於圖像金字塔的圖像向下取樣和向上取樣
- 帶你掌握如何使用CANN 算子ST測試工具msopst
- 一招教你如何高效批量導入與更新數據
- 一步步搞懂MySQL元數據鎖(MDL)
- 你知道如何用 PHP 實現多進程嗎?
- KubeSphere 網關的設計與實現(解讀)