Java 設計模式——狀態模式

概述

很多人在說狀態模式的時候總拿策略模式來進行對比,可能他們的類圖會有一點類似,可我卻不認爲他們有多麼相像。你可以閱讀《Java設計模式——策略模式》這篇博客,並與本文對比,以找到蛛絲馬跡。
他們最根本的差異在於策略模式是在求解同一個問題的多種解法,這些不同解法之間毫無關聯;狀態模式則不同,狀態模式要求各個狀態之間有所關聯,以便實現狀態轉移。

定義

狀態模式(State),當一個對象的內部狀態改變時允許改變其行爲,這個對象看起來像是改變了其類。


目錄


版權說明

著作權歸作者所有。
商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
本文作者:Coding-Naga
發表日期: 2016年6月6日
本文鏈接:http://blog.csdn.net/lemon_tree12138/article/details/51596556
來源:CSDN
更多內容:分類 >> 設計模式


狀態模式

我很喜歡具有狀態轉移的程序,總是感覺這裏充滿了無限的魅力。如果你也對狀態轉移的邏輯感興趣,那麼你可以閱讀一下我之前的幾篇博客。

情境

看《Java 設計模式》的時候,我看到一個例子,感覺很好,拿來跟大家一起分享一下。
實體是電梯,這個大家一定不陌生。我們知道電梯主要有4種狀態:電梯門關閉、電梯門打開、電梯上下運載、電梯停止。而且我們知道,電梯在門打開的時候,只能是關閉電梯門,不能是其他的任何操作。在學習狀態模式之前,如果我們要編寫這個邏輯,一定是長篇累讀地 if … else … 。而且邏輯混亂,很難維護。當然,這裏你可以使用 if … else …,因爲電梯的這些狀態基本是穩定的,不會有什麼變動。而如果你的需求裏,狀態會不斷更新,而你之前使用 if … else … 埋下的患根這時就會讓你苦不堪言。
所以,你需要重構你的代碼。

狀態模式類圖

這裏寫圖片描述

邏輯實現

如果想要避免使用 if … else … 或是 switch … case …,那麼我們就需要對這些條件進行封裝。在學習狀態模式之前,我很喜歡使用一個 Map 來解決 switch … case … 問題,而且屢試不爽。從使用 Map 來解決 switch … case … 問題中可以知道,這裏的條件類必須去繼承一個共同的類或是共同的接口。這裏就是上面類圖中的 LiftState。
LiftState.java

public abstract class LiftState {

    protected Context context;

    public void setContext(Context _context) {
        this.context = _context;
    }

    public abstract void open();

    public abstract void close();

    public abstract void run();

    public abstract void stop();
}

可能你很奇怪,爲什麼這裏 LiftState 類裏面會有一個 Context 對象。它的作用是去調節狀態的變化,它就是電梯,你的電梯狀態肯定是針對電梯來說的,所以組合一個 Context 一點也不奇怪。

現在來看看 LiftState 的實現類吧,就拿 StoppingState 類來說吧,其他的實現跟這個類很像,就不多貼代碼了。想要詳細代碼的朋友可以去我的 GitHub 上下載。
StoppingState.java

public class StoppingState extends LiftState {

    @Override
    public void close() {
        // do nothing;
    }

    @Override
    public void open() {
        super.context.setLiftState(Context.openningState);
        super.context.getLiftState().open();
    }

    @Override
    public void run() {
        super.context.setLiftState(Context.runningState);
        super.context.getLiftState().run();
    }

    @Override
    public void stop() {
        System.out.println("電梯停止了...");
    }
}

在停下來的時候,我們不能讓電梯關閉,因爲它原本就是關閉的,我這裏做法是不處理,當然你可以選擇拋出異常。當電梯停下來的時候,電梯是可以打開的,所以在 open() 方法裏可以將電梯的狀態標識爲打開狀態;當然,也可以標識爲運載狀態。而究竟會轉換成哪一種狀態,就要依據實際乘客的使用情況了。
下面看看我們的關鍵實體 Context 是怎麼實現的。
Context.java

public class Context {

    // 定義出所有的電梯狀態
    public final static OpenningState openningState = new OpenningState();
    public final static ClosingState closeingState = new ClosingState();
    public final static RunningState runningState = new RunningState();
    public final static StoppingState stoppingState = new StoppingState();

    // 定一個當前電梯狀態
    private LiftState liftState;

    public LiftState getLiftState() {
        return liftState;
    }

    public void setLiftState(LiftState liftState) {
        this.liftState = liftState;
        // 把當前的環境通知到各個實現類中
        this.liftState.setContext(this);
    }

    public void open() {
        this.liftState.open();
    }

    public void close() {
        this.liftState.close();
    }

    public void run() {
        this.liftState.run();
    }

    public void stop() {
        this.liftState.stop();
    }
}

Context 組合了所有狀態,這一點不奇怪,因爲它是電梯嘛。在上面的代碼中,你們可能很迷惑,這裏 Context 都是去調用 LiftState 接口的相應方法,哪裏體現了狀態的轉移呢?其實狀態轉移的邏輯是在各自的狀態裏面進行的,就像上面的 StoppingState 類。如果調用了 StoppingState 類,是不是說當前 Context 裏的狀態是 StoppingState 呢?而它卻在 open() 方法裏將 Context 的狀態轉換成了 OpenningState 。這樣就完成了狀態的轉換了。Context 類的作用我想只是去觸發狀態的轉換。
下面提供一張電梯的狀態轉移圖:

這裏寫圖片描述


Ref

  • 《Java 設計模式》

GitHub 源碼

https://github.com/William-Hai/DesignPatternCollections


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章