實際開發中訂單往往都包含著訂單狀態,使用者每進行一次操作都要切換對應的狀態,而每次切換判斷當前的狀態是必須的,就不可避免的引入一系列判斷語句,為了讓程式碼更加清晰直觀,我們引入今天的主角——狀態模式。
假設訂單狀態有,下單、發貨、確認收貨,如果使用者確認收貨,在常規程式設計中就要判斷當前使用者的狀態,然後再修改狀態,如果這種情況下使用狀態模式。
將各個狀態都抽象成一個狀態類,比如下單狀態類、發貨狀態類、確認收貨類,在狀態類中處理相應的邏輯和控制下一個狀態,在定義一個環境類,定義初始狀態,並控制切換狀態。
在狀態模式中應該包含著三個角色:
環境類(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 等冗餘語句,程式碼更有層次性,並且具備良好的擴充套件力。
比如審批流程,我們案例也僅僅是用於訂單流程做例子,在實際開發中並不會使用這種方式處理訂單,因為訂單的處理邏輯實際上並不是那麼複雜,引入狀態模式反而增加了更多的類,造成系統更加的複雜,這也是設計模式最顯著的缺點。
設計模式的學習要成體系,推薦你看我往期釋出的設計模式文章。