行爲型模式之狀態模式


在軟件開發過程中,應用程序中的有些對象可能會根據不同的情況做出不同的行爲,我們把這種對象稱爲有狀態的對象,而把影響對象行爲的一個或多個動態變化的屬性稱爲狀態。當有狀態的對象與外部事件產生互動時,其內部狀態會發生改變,從而使得其行爲也隨之發生改變。如人的情緒有高興的時候和傷心的時候,不同的情緒有不同的行爲,當然外界也會影響其情緒變化。

對這種有狀態的對象編程,傳統的解決方案是:將這些所有可能發生的情況全都考慮到,然後使用 if-else 語句來做狀態判斷,再進行不同情況的處理。但當對象的狀態很多時,程序會變得很複雜。而且增加新的狀態要添加新的 if-else 語句,這違背了“開閉原則”,不利於程序的擴展。

以上問題如果採用“狀態模式”就能很好地得到解決。狀態模式的解決思想是:當控制一個對象狀態轉換的條件表達式過於複雜時,把相關“判斷邏輯”提取出來,放到一系列的狀態類當中,這樣可以把原來複雜的邏輯判斷簡單化。

狀態模式的定義

狀態(State)模式的定義:對有狀態的對象,把複雜的“判斷邏輯”提取到不同的狀態對象中,允許狀態對象在其內部狀態發生改變時改變其行爲。也稱狀態機模式,屬於行爲性模式。

狀態模式類的行爲是由狀態決定的,不同的狀態下有不同的行爲。其意圖是讓一個對象在其內部改變的時候,其行爲也隨之改變。狀態模式的核心是狀態與行爲綁定,不同的狀態對應不同的行爲。

狀態模式的結構

狀態模式包含以下主要角色:
環境(Context)角色:也稱爲上下文,它定義了客戶感興趣的接口,維護一個當前狀態,並將與狀態相關的操作委託給當前狀態對象來處理。
抽象狀態(State)角色:定義一個接口或抽象類,用以封裝環境對象中的特定狀態所對應的行爲。
具體狀態(Concrete State)角色:實現抽象狀態所對應的行爲。

狀態模式的實現

舉例:用戶收藏和評論文章,未登錄狀態時直接執行登錄的操作,已登錄時執行收藏和評論。

/**
 * 抽象狀態(`State`)角色:  用戶狀態
 */
public abstract class UserState {
    protected AppContext context;
    public void setContext(AppContext context) {
        this.context = context;
    }
    public abstract void favorite();
    public abstract void comment(String comment);
}
/**
 * 具體狀態(`Concrete State`)角色: 未登錄的狀態
 */
public class UnLoginState extends UserState {
    @Override
    public void favorite() {
        this.switch2login();//未登錄時 執行跳轉登錄頁面
        this.context.getState().favorite(); //執行當前狀態(已登錄)要執行的邏輯
    }
    @Override
    public void comment(String comment) {
        this.switch2login();//未登錄時 執行跳轉登錄頁面
        this.context.getState().comment(comment);//執行當前狀態(已登錄)要執行的邏輯
    }
    private void switch2login(){
        System.out.println("跳轉到登錄頁!");
        this.context.setState(this.context.STATE_LOGIN);//登錄完成後,設置狀態爲已登錄
    }
}
/**
 * 具體狀態(`Concrete State`)角色: 已登錄狀態
 */
public class LoginState extends UserState {
    @Override
    public void favorite() {
        System.out.println("收藏成功!");
    }
    @Override
    public void comment(String comment) {
        System.out.println(comment);//展示評論
    }
}
/**
 * 環境(`Context`)角色: 上下文,維護一個當前狀態,對外調用
 */
public class AppContext {
    public static final UserState STATE_LOGIN = new LoginState(); //登錄狀態
    public static final UserState STATE_UNLOGIN = new UnLoginState();//未登錄狀態
    private UserState currentState = STATE_UNLOGIN;//當前狀態,默認是未登錄

    {
        //初始化具體狀態的AppContext
        STATE_LOGIN.setContext(this);
        STATE_UNLOGIN.setContext(this);
    }

    public void setState(UserState state){
        this.currentState = state;//設置當前狀態
    }

    public UserState getState(){
        return this.currentState;//獲取當前狀態
    }

    public void favorite(){
        this.currentState.favorite();//執行當前狀態的favorite()方法
    }

    public void comment(String comment){
        this.currentState.comment(comment);//執行當前狀態的comment()方法
    }
}
/**
 * 測試類
 */
public class Test {
    public static void main(String[] args) {
        AppContext context = new AppContext();
        context.favorite();
        context.comment("評論:好文章,360個贊");
    }
}

程序運行結果如下:

跳轉到登錄頁!
收藏成功!
評論:好文章,666個贊

狀態模式的應用場景

在軟件開發過程中,對於某一項操作,可能存在不同的情況。通常處理多情況問題最直接的方式就是使用
if…else或switch…case條件語句進行枚舉。但是這種做法對於複雜狀態的判斷天然存在弊端:條件判斷語句過於臃腫,可讀性差,且不具備擴展性,維護難度也大。而如果轉換思維,將這些不同狀態獨立起來用各個不同的類進行表示,系統處於哪種情況,直接使用相應的狀態類對象進行處理,消除了if…else,switch…case等冗餘語句,代碼更有層次性且具備良好擴展力。

狀態模式主要解決的就是當控制一個對象狀態的條件表達式過於複雜時的情況。通過把狀態的判斷邏輯轉移到表示不同狀態的一系列類中,可以把複雜的判斷邏輯簡化。對象的行爲依賴於它的狀態(屬性),並且會根據它的狀態改變而改變它的相關行爲。狀態模式適用於以下場景:
1、行爲隨狀態改變而改變的場景;
2、一個操作中含有龐大的多分支結構,並且這些分支取決於對象的狀態。

狀態模式的優缺點

狀態模式其主要優點如下:
1.狀態模式將與特定狀態相關的行爲局部化到一個狀態中,並且將不同狀態的行爲分割開來,滿足“單一職責原則”。
2.減少對象間的相互依賴。將不同的狀態引入獨立的對象中會使得狀態轉換變得更加明確,且減少對象間的相互依賴。
3.有利於程序的擴展。通過定義新的子類很容易地增加新的狀態和轉換。

狀態模式的主要缺點如下:
1.狀態模式的使用必然會增加系統的類與對象的個數。
2.狀態模式的結構與實現都較爲複雜,如果使用不當會導致程序結構和代碼的混亂。

<<上一篇:責任鏈模式

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