設計模式詳解——狀態模式


前言

今天我們來看一個號稱策略模式雙胞胎的設計模式——狀態模式,如它的名字一樣,狀態模式最核心的設計思路就是將對象的狀態抽象出一個接口,然后根據它的不同狀態封裝其行為,這樣就可以實現狀態和行為的綁定,最終實現對象和狀態的有效解耦。下面我們就來詳細看下它的基本原理和實現過程吧。

狀態模式

狀態模式允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了它的類。

要點

  • 狀態模式允許一個對象基於內部狀態而擁有不同的行為
  • 和程序狀態機(PSM)不同,狀態模式用類代表狀態
  • Context會將行為委托給當前狀態對象
  • 通過將每個狀態封裝進一個類,我們把以后需要做的任何改變局部化了
  • 狀態模式和策略模式有相同的類圖,但是它們的意圖不同
  • 策略模式通常會用行為或算法來配置Context
  • 狀態模式允許Context隨着狀態的改變而改變行為
  • 狀態轉換可以由State類或Context類控制
  • 使用狀態模式通常會導致設計中類的數目大量增加
  • 狀態類可以被多個Context示例共享

優缺點

優點
  1. 封裝了轉換規則。

  2. 枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。

  3. 將所有與某個狀態有關的行為放到一個類中,並且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為。

  4. 允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊

  5. 可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。

缺點
  1. 狀態模式的使用必然會增加系統類和對象的個數。
  2. 狀態模式的結構與實現都較為復雜,如果使用不當將導致程序結構和代碼的混亂。
  3. 狀態模式對"開閉原則"的支持並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的源代碼。

使用場景

  1. 行為隨狀態改變而改變的場景。
  2. 條件、分支語句的代替者。

示例

狀態接口

首先是狀態接口,這個接口是給我們實際的狀態對象繼承的,這個接口有一個方法doAction,這個方法就是給不同的狀態對象實現的,用於處理不同狀態下的行為的。

public interface State {
    /**
     * 改變狀態的操作
     * @param context
     */
    void doAction(Context context);
}
狀態所屬者

然后是我們的狀態所屬者,這個類有一個核心的屬性就是我們的State接口。

public class Context {
    private State state;

    public Context(){}

    public void setState(State state){
        this.state = state;
    }

    public State getState(){
        return state;
    }
     @Override
    public String toString() {
        return "Context{" +
                "state=" + state +
                '}';
    }
}
狀態實現

狀態實現者繼承了State接口,並實現了doAction方法,在方法內部可以對我們的狀態所有者進行對應的操作。

這里是一個啟動狀態:

public class StopState implements State {

    private String name;

    public StopState() {
        this.name = "stop";
    }

    @Override
    public void doAction(Context context) {
        System.out.println("Context is in stop state");
        context.setState(this);
        System.out.println(context);
    }

    @Override
    public String toString() {
        return "StopState{" +
                "name='" + name + '\'' +
                '}';
    }
}

這里是停止狀態

public class StartState implements State{

    private String name;

    public StartState() {
        this.name = "start";
    }

    @Override
    public void doAction(Context context) {
        System.out.println("Context is in start state");
        context.setState(this);
        System.out.println(context);
    }

    @Override
    public String toString() {
        return "StartState{" +
                "name='" + name + '\'' +
                '}';
    }
}
測試代碼

這里分別實例化了容器和狀態的示例,然后通過示例的doAction方法操作容器

@Test
    public void testState() {
        Context context = new Context();

        StartState startState = new StartState();
        startState.doAction(context);


        StopState stopState = new StopState();
        stopState.doAction(context);

    }
運行結果

可以看到,狀態對象的doAction方法執行后,容器對應的狀態也發生了改變:

好了,關於狀態模式就先說這么多,接下來我們做一個簡單的總結。

總結

有用過策略模式或者對策略模式比較熟悉的小伙伴應該發現了:策略模式其實和我們今天分析狀態模式特別像,甚至連架構模式都是一樣的,所以這里我們有必要說下它們的區別。

首先是策略模式,它其實是將不同的算法封裝成不同的策略,然后在具體的策略中實現具體的行為,但是測試本身是被動被選擇的,容器選擇策略,調用過程發生在容器中,而且策略本身是入參;

而我們今天分析的狀態模式,它是將不同狀態對應的行為封裝,然后由具體的狀態操作容器,整個過程更像是狀態主動發起的,由狀態執行其自己的方法,入參是容器。

這兩種設計模式從某種程度上說是可以互相替換的,但是還是要結合具體業務分析的,比如spring boot啟動過程中,它用到的就是狀態模式,這一點我們在分析spring boot啟動過程中也發現了;但如果是涉及到算法層面的內容,比如兩個數的加減乘除,顯然策略模式才是更好的選擇。

總之,學習設計模式除了要了解它的基本原理和應用場景之外,更重要的是,要學會辨識優秀框架中的設計模式(知識,知就是知道,了解,識局勢辨識,分析),最終將這些設計模式應用到我們的業務開發之中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM