設計模式之狀態模式---State Pattern

hfreeman2008發表於2016-08-05

模式的定義

狀態模式定義如下:

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

當一個物件內在狀態改變時允許其改變行為,這個物件看起來像改變了其類

模式的使用場景

  • 行為隨狀態改變而改變的場景

這就是狀態模式的根本出發點,例如許可權設計,人員的狀態不同即使執行相同的行為結果也會不同,在這種情況下需要考慮使用狀態模式

  • 條件,分支判斷語句的替代者

在程式中大量使用switch語句或者if判斷語句會導致程式結構不清晰,邏輯混亂,使用狀態模式可以很好的避免這一問題,它通過擴充套件子類來實現對條件的判斷處理。

UML類圖

這裡寫圖片描述

角色介紹

  • State—-抽象狀態角色

介面或抽象類,負責物件狀態定義,並且封裝環境角色以實現狀態切換。

  • ConcreteState—-具體狀態角色

每一個具體狀態必須完成二個職責:本狀態的行為以及趨向狀態處理,也就是本狀態下要做的事,以及本狀態如何過渡到其它狀態。

Context—-環境角色
定義客戶端需要的介面,並且負責具體的狀態切換。

狀態模式相對來說比較複雜,它提供了一種對物質運動的另一個觀察視角,通過對狀態變更促進行為的變化,就類似水的狀態變更一樣,一碗水的初始狀態是液態,通過加熱轉變為氣態,狀態的變化同時也引起了體積的擴大,然後就產生了新的行為。

模式的通用原始碼

State—抽象狀態角色
抽象State中宣告一個環境角色,提供各個狀態類自行訪問,並且提供所有狀態的抽象行為,由各個實現類實現。

public abstract class State {
    //定義一個環境角色,提供子類訪問
    protected Context context;
    //設定環境角色
    public void setContext(Context context){
        this.context = context;
    }
    //行為1
    public abstract void handle1();
    //行為2
    public abstract void handle2();

}

ConcreteState1 —具體狀態1,handle1處理本狀態的行為,handle2處理狀態過渡

public class ConcreteState1 extends State{

    @Override
    public void handle1() {
        // TODO Auto-generated method stub
        System.out.println("ConcreteState1---handle1");
    }

    @Override
    public void handle2() {
        // TODO Auto-generated method stub
        System.out.println("ConcreteState1---handle2");
        super.context.setCurrentState(Context.STATE2);
        super.context.handle2();
    }

}

ConcreteState2 —具體狀態2,handle2處理本狀態的行為,handle1處理狀態過渡


public class ConcreteState2 extends State{

    @Override
    public void handle1() {
        // TODO Auto-generated method stub
        System.out.println("ConcreteState2---handle1");
        super.context.setCurrentState(Context.STATE1);
        super.context.handle1();
    }

    @Override
    public void handle2() {
        // TODO Auto-generated method stub
        System.out.println("ConcreteState2---handle2");
    }

}

具體狀態有二個職責,處理本狀態必須完成的行為,決定是否可以過濾到其它狀態。

Context —-環境角色

public class Context {
    //定義狀態
    public final static State STATE1 = new ConcreteState1();
    public final static State STATE2 = new ConcreteState2();
    //當前狀態
    private State currentState;
    //獲得當前狀態
    public State getState(){
        return this.currentState;
    }
    //設定當前狀態
    public void setCurrentState(State state){
        this.currentState = state;
        //切換狀態
        this.currentState.setContext(this);
    }
    //行為委託
    public void handle1(){
        this.currentState.handle1();
    }
    //行為委託
    public void handle2(){
        this.currentState.handle2();
    }

}

環境角色有二個不成文的約束:

  1. 把狀態物件宣告為靜態常量,有幾個狀態物件就宣告幾個靜態常量
  2. 環境角色具有狀態抽象角色定義的所有行為,具體執行使用委託的方式

Client —客戶呼叫端:

public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Context context = new Context();
        context.setCurrentState(new ConcreteState1());

        System.out.println("Client---context.handle1()");
        context.handle1();

        System.out.println("Client---context.handle2()");
        context.handle2();
    }
}

程式輸出:

Client---context.handle1()
ConcreteState1---handle1

Client---context.handle2()
ConcreteState1---handle2
ConcreteState2---handle2

優點

  • 結構清晰

避免了過多的switch case 或者 if else語句的使用,避免了程式的複雜性,提高系統的可維護性

  • 遵循設計原則

很好的體現了開閉原則和單一職責原則,每個狀態都是一個子類,你要增加狀態就要增加子類,你要修改狀態,你只修改一個子類就可以了。

  • 封裝性好

狀態變換放置到類的內部來實現,外部的呼叫不用知道類內部如何實現狀態和行為的變換。

缺點

子類太多,就是類膨脹。如果一個事物有很多個狀態,那麼我們使用狀態模式就會有太多的子類,不好管理。這個問題需要大家在專案中自己衡量。當然,解決此問題也有許多方法,比如在資料庫中建立一個狀態表,然後根據狀態執行相應的操作。

注意事項

狀態模式適用於當物件在它的狀態發生改變時,它的行為也隨著發生比較大的改變,也就是說行為受狀態約束的情況下可以使用狀態模式,而且使用時物件的狀態最好不要超過5個。

Android原始碼中的模式實現

UML類圖

具體實現程式碼

參考資料

(1).設計模式之禪—第26章 狀態模式
(2)狀態模式
https://github.com/simple-android-framework/android_design_patterns_analysis/tree/master/state

相關文章