首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

設計模式精解:State 模式——狀態驅動行為的優雅實作

     編輯:Java 相關知識

State 模式定義

不同狀態,不同行為;或說,每個狀態擁有相應行為。

何時使用?

State 模式實際使用頻率高,適合「狀態切換」。因我們常用 if-else if-else 進行狀態切換,若針對狀態的判斷切換反覆出現,就要聯想是否可採用 State 模式。

不只根據狀態,也有根據屬性。若某物件屬性不同導致行為不同,在資料庫系統中出現頻率較高。我們常在資料表尾部加上 property 屬性欄位標識特殊性質記錄,此屬性改變(切換)隨時可能發生,就有可能使用 State。

是否一定要使用?

實際中類似開關的狀態切換很多,但有時不明顯,取決於經驗與對系統理解深度。

這裡要闡述「開關切換狀態」與「一般狀態判斷」有區別。「一般狀態判斷」也是 if-else if 結構,例:

if (which == 1) state = "hello";
else if (which == 2) state = "hi";
else if (which == 3) state = "bye";

這是「一般狀態判斷」,state 值根據 which 變數決定,which 與 state 無關。若改成:

if (state.equals("bye")) state = "hello";
else if (state.equals("hello")) state = "hi";
else if (state.equals("hi")) state = "bye";

這就是「開關切換狀態」,將 state 從 hello 切換到 hi,再到 bye,再回 hello,像旋轉開關,這種狀態改變可用 State 模式。

若單純只有 hello → hi → bye → hello 單一方向切換,不一定需要 State 模式,因 State 模式會建立許多子類別,增加複雜度。但若發生反方向切換、或需任意切換,就需要 State 了。

實例分析

public class Context {
    private Color state = null;
    public void push() {
        // 如果當前 red 狀態就切換到 blue
        if (state == Color.red) state = Color.blue;
        // 如果當前 blue 狀態就切換到 green
        else if (state == Color.blue) state = Color.green;
        // 如果當前 black 狀態就切換到 red
        else if (state == Color.black) state = Color.red;
        // 如果當前 green 狀態就切換到 black
        else if (state == Color.green) state = Color.black;

        Sample sample = new Sample(state);
        sample.operate();
    }
    public void pull() {
        // 與 push 狀態切換正好相反
        if (state == Color.green) state = Color.blue;
        else if (state == Color.black) state = Color.green;
        else if (state == Color.blue) state = Color.red;
        else if (state == Color.red) state = Color.black;
        Sample2 sample2 = new Sample2(state);
        sample2.operate();
    }
}

上例有兩個動作 push 推與 pull 拉,改變 Context 顏色,至此需要 State 模式優化。但注意:本例 state 變化只是簡單顏色賦值,具體行為很簡單,State 適合龐大具體行為,因此實際使用不一定非要用 State 模式,會增加子類別數目、讓簡單變複雜。

適合 State 模式的經典案例

  • 銀行帳戶:常在 Open 與 Close 狀態間轉換。
  • TcpConnection:狀態有建立、偵聽、關閉三個,反覆轉換,具體行為非一兩句可完成。
  • 信箱 POP 帳號:四種狀態 start、HaveUsername、Authorized、quit,每個狀態對應行為較大。
  • 繪圖程式工具箱:用戶選擇不同工具繪製方框、直線、曲線,狀態切換適合 State。

如何使用

State 需兩類實體參與:

  1. State Manager(狀態管理器):即開關,如上例 Context 實為 State Manager,其中有狀態切換動作。
  2. 抽象類別或介面實作的父類別:不同狀態為繼承此父類別的不同子類別。

第一步:建立狀態父類別

public abstract class State {
    public abstract void handlePush(Context c);
    public abstract void handlePull(Context c);
    public abstract Color getColor();
}

父類別方法對應 State Manager 中開關行為。本例 Context 有 push、pull 兩動作,狀態父類別需具體處理:handlePush()、handlePull();同時需取得推/拉結果方法 getColor()。

第二步:具體狀態子類別實作

public class BlueState extends State {
    public void handlePush(Context c) {
        // 根據 push 方法「如果是 blue 狀態切換到 green」
        c.setState(new GreenState());
    }
    public void handlePull(Context c) {
        // 根據 pull 方法「如果是 blue 狀態切換到 red」
        c.setState(new RedState());
    }
    public Color getColor() { return Color.blue; }
}

其他狀態子類別(RedState、GreenState、BlackState)實作類似。

第三步:重寫 State Manager (Context)

public class Context {
    private State state = null; // 原 Color state 改為新 State state

    // setState 用來改變 state 狀態,實現狀態切換
    public void setState(State state) {
        this.state = state;
    }

    public void push() {
        // 狀態切換細節(本例顏色變化)已封裝在子類別 handlePush 中,這裡無需關心
        state.handlePush(this);

        // Sample 需使用 state 切換結果,用 getColor()
        Sample sample = new Sample(state.getColor());
        sample.operate();
    }

    public void pull() {
        state.handlePull(this);
        Sample2 sample2 = new Sample2(state.getColor());
        sample2.operate();
    }
}

至此完成 State 重構過程。以上為簡單實例,實際應用中 handlePush、handlePull 處理較複雜。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved