設計模式之狀態模式
實際開發中訂單往往都包含著訂單狀態,使用者每進行一次操作都要切換對應的狀態,而每次切換判斷當前的狀態是必須的,就不可避免的引入一系列判斷語句,為了讓程式碼更加清晰直觀,我們引入今天的主角——狀態模式。
一、概念理解
假設訂單狀態有,下單、發貨、確認收貨,如果使用者確認收貨,在常規程式設計中就要判斷當前使用者的狀態,然後再修改狀態,如果這種情況下使用狀態模式。
將各個狀態都抽象成一個狀態類,比如下單狀態類、發貨狀態類、確認收貨類,在狀態類中處理相應的邏輯和控制下一個狀態,在定義一個環境類,定義初始狀態,並控制切換狀態。
在狀態模式中應該包含著三個角色:
環境類(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 閘道器的設計與實現(解讀)