設計模式之——狀態模式

程式人生-vincent發表於2020-11-11

物件的行為依賴於它的狀態(屬性),並且可以根據它的狀態改變而改變它的相關行為 ,屬於行為型模式,允許一個物件在其內部狀態改變時改變它的行為

狀態模式是策略模式的孿生兄弟,它們的UML圖是一樣的,但實際上解決的是不同情況的兩種場景問題,工作中用的不多,基本是策略模式比較多

應用場景

  • 一個物件的行為取決於它的狀態, 並且它必須在執行時刻根據狀態改變它的行為
  • 程式碼中包含大量與物件狀態有關的條件語句 ,比如一個操作中含有龐大的多分支的條件if else語句,且這些分支依賴於該物件的狀態
  • 電商訂單狀態:未支付、已支付、派送中,收貨完成等狀態,各個狀態下處理不同的事情

角色

  • Context 上下文:定義了客戶程式需要的介面並維護一個具體狀態角色的例項,將與狀態相關的操作委託給當前的Concrete State物件來處理
  • State 抽象狀態類: 定義一個介面以封裝與Context的一個特定狀態相關的行為
  • ConcreteState具體狀態類: 實現抽象狀態定義的介面

程式碼實現

以電商訂單狀態流轉為例,每步都有不同的操作內容: 新建訂單/已支付/已發貨/確認收貨
public interface State {
​
    void handle();
}

介面實現

public class NewOrderState implements State{
​
    @Override
    public void handle() {
        System.out.println("新訂單,未支付");
        System.out.println("呼叫商戶客服服務,有新訂單\n");
    }
}
public class PayOrderState implements State{
​
    @Override
    public void handle() {
        System.out.println("新訂單已經支付");
        System.out.println("呼叫商戶客服服務,訂單已經支付");
        System.out.println("呼叫物流服務,未發貨\n");
    }
}
public class SendOrderState implements State{
​
    @Override
    public void handle() {
        System.out.println("訂單已經發貨");
        System.out.println("呼叫簡訊服務,告訴使用者已經發貨");
        System.out.println("更新物流資訊\n");
    }
}
public class OrderContext {
​
    private State state;
​
    public OrderContext(){}
​
    public void setState(State state) {
        this.state = state;
        System.out.println("訂單狀態變更!!");
        this.state.handle();
    }
}

測試

public static void main(String[] args) {
​
    OrderContext orderContext = new OrderContext();
​
    orderContext.setState(new NewOrderState());
​
    orderContext.setState(new PayOrderState());
​
    orderContext.setState(new SendOrderState());
​}

優點

1、結構清晰:將狀態類獨立為類,消除了冗餘的if...else 或 switch...case 語句,使程式碼更加簡潔,提高系統可維護性

2、將狀態轉換顯示化:通常的物件內部都是使用數值型別來定義狀態,狀態的切換是通過賦值進行表現,不夠直觀;而使用狀態類,在切換時,是以不同的類進行表示,轉換目的更加明確

3、狀態類職責明確且具備擴充套件性

缺點

1、類膨脹:如果一個事物具備很多狀態,則會造成狀態類太多

2、狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和程式碼的婚禮

3、狀態模式對開閉原則的支援並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的原始碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需要修改對應類的原始碼

相關文章