天天看點

行為型模式之狀态模式(State)

狀态模式,将條件的判斷從業務邏輯中分離出去,或者說将狀态的轉換規則分離出去。

程式運作中所依賴的條件可視作程式的狀态,程式的運作邏輯非常複雜,在不同的狀态下需要有不同的表現,甚至某狀态的作用要依賴另外的狀态,狀态模式将程式“狀态”(即程式運作條件)進行了抽象,這樣,通過自由切換狀态條件可以友善地得到不同的表現。

我們在程式設計過程中,經常會涉及到在某些狀态下進行特定的操作,一般我們會傳入一個條件,然後對條件進行判斷,如果是...則...,通常都會首先想到if..else..的編碼方式,這種if...else的思維方式不符合面向對象的思維方式,因為這種思維是面向過程的!

這種方式的缺點很明顯:

1.如果有新條件的加入則需要修改主邏輯,添加新的if..else,不符合開閉原則。

2.如果狀态間有依賴關系,比如先後順序,不容易展現和維護。

如果你的代碼中出現了大片的 if ..else 那麼就應該考慮使用 狀态模式了,如過我們需要根據某某的狀态進行操作,當它是什麼的時候我們進行某某操作..我們還需要狀态可以切換的,可以回退到上個狀态,我們還希望能在這個狀态進行完乎自動切到下個狀态工作,它們的順序是需要保證的等等,那麼就應該考慮使用 狀态模式了。

如果我們的程式要适應複雜的“狀态變化”,那麼就要使用狀态模式了。

狀态模式把 狀态的變化規則 從業務邏輯中抽取出來,進而可以友善地改變狀态,進而驅動不同的業務邏輯。

狀态模式的實作通常會有一個“狀态管理器”,用于管理既有狀态以及将狀态轉換到原有業務邏輯中去。

轉換規則可以在具體的狀态中或狀态管理器中來實作,進而與原有業務邏輯相分離。

場景:電視機在不同的頻道下播放不同的節目,且我們假設老式的電視訊道隻能往前或往後順序選擇,不能随意選頻道(即頻道之間有依賴關系),根據不同的頻道電視機通過查找、接收、處理等複雜的邏輯将節目播放出來。

設計:

行為型模式之狀态模式(State)

示例代碼:

interface ChannelState {
    String show();
    void push(ChannelStateContext ctx);
    void pull(ChannelStateContext ctx);
}
class ChannelStateContext {
    ChannelState currentState;
    ChannelStateContext(ChannelState channelState) {
        this.currentState = channelState;
    }
    void changeToNext() {
        currentState.push(this);
    }
    void changeToLast() {
        currentState.pull(this);
    }
    void setChannelState(ChannelState state) {
        currentState = state;
    }
    String action() {
        StringBuilder result = new StringBuilder("===查找信号,接收信号,處理信号... 播放節目:");// TODO 調用其它業務邏輯
        result.append(currentState.show());//調用目前狀态
        return result.toString();
    }
}
class MovieChannel implements ChannelState {
    public String show() {
        return "電影";
    }
    public void push(ChannelStateContext ctx) {
        ctx.setChannelState(new MusicChannel());
    }
    public void pull(ChannelStateContext ctx) {
        ctx.setChannelState(new OtherChannel());
    }
}
class MusicChannel implements ChannelState {
    public String show() {
        return "音樂";
    }
    public void push(ChannelStateContext ctx) {
        ctx.setChannelState(new OtherChannel());
    }
    public void pull(ChannelStateContext ctx) {
        ctx.setChannelState(new MovieChannel());
    }
}
class OtherChannel implements ChannelState {
    public String show() {
        return "其它";
    }
    public void push(ChannelStateContext ctx) {
        ctx.setChannelState(new MovieChannel());
    }
    public void pull(ChannelStateContext ctx) {
        ctx.setChannelState(new MusicChannel());
    }
}
public class Test {
    public static void main(String[] args) {
        // 初始狀态為電影,也可以是其它其它狀态
        ChannelStateContext channelStateContext = new ChannelStateContext(new MovieChannel());// 電影
        System.out.println(channelStateContext.action());
        // 向後切換
        channelStateContext.changeToNext();// 音樂
        System.out.println(channelStateContext.action());

        channelStateContext.changeToNext();// 其它
        System.out.println(channelStateContext.action());

        channelStateContext.changeToNext();// 電影
        System.out.println(channelStateContext.action());

        // 向前切換
        channelStateContext.changeToLast();// 其它
        System.out.println(channelStateContext.action());

        channelStateContext.changeToLast();// 音樂
        System.out.println(channelStateContext.action());

        channelStateContext.changeToLast();// 電影
        System.out.println(channelStateContext.action());
    }
}
           

輸出:

===查找信号,接收信号,處理信号... 播放節目:電影
===查找信号,接收信号,處理信号... 播放節目:音樂
===查找信号,接收信号,處理信号... 播放節目:其它
===查找信号,接收信号,處理信号... 播放節目:電影
===查找信号,接收信号,處理信号... 播放節目:其它
===查找信号,接收信号,處理信号... 播放節目:音樂
===查找信号,接收信号,處理信号... 播放節目:電影
           

狀态模式與政策模式的差別,參見:政策模式。

繼續閱讀