1. 程式人生 > >Java設計模式學習記錄-狀態模式

Java設計模式學習記錄-狀態模式

前言

狀態模式是一種行為模式,用於解決系統中複雜的物件狀態轉換以及各個狀態下的封裝等問題。狀態模式是將一個物件的狀態從該物件中分離出來,封裝到專門的狀態類中,使得物件的狀態可以靈活多變。這樣在客戶端使用時無需關心物件的狀態,可以實現自身的一致性處理。最近工作有些忙,更新部落格慢了。還是要嚴格要求自己的,抽時間也要堅持學習。 

狀態模式

概念介紹

狀態模式允許一個物件在其狀態改變時,改變它的行為,物件看起來似乎修改了它的類。

想要在改變自身狀態時改變物件行為,最直接的方法就是在程式碼中將所有可能發生的情況都考慮到了,然後使用if-else語句來進行相應的選擇。但是這種方法對於複雜的狀態判斷會顯得雜亂無章,容易產生錯誤;而且增加一個新的狀態將會帶來大量的修改。

流程結構如下圖:

此時“能夠修改自身”的狀態模式的引入也許是個不錯的主意,將不同條件下的行為封裝在一個類裡,再給這些類一個統一的父類來約束它們。

就會變成如下圖流程結構:

舉例

還是來舉個具體的例項來說明一下吧。同事小王要請假,根據公司規定,請假要先在OA上提請假申請單,然後審批通過後就可以正常休假了。這個請假申請單大致經歷這麼幾個狀態,未稽核(待提交)、稽核中、稽核通過、稽核未通過,這裡只是粗略的分為這幾個狀態。下面我們使用狀態模式來模擬實現一下這個請假申請單的狀態流轉過程。

先建立一個休假申請單類

/**
 * 休假申請
 */
public class LeaveApply {
    
/** * 休假申請單初始狀態是待提交狀態 */ private ApplyState applyState = new UnAudited(); /** * 設定狀態 * @param state */ public void setState(ApplyState state){ applyState = state; } /** * 狀態變化後,更新物件自身的行為 */ public void update(){ applyState.changeHandle(); } }

審批單狀態介面,宣告統一處理的方法。

/**
 * 審批單狀態介面
 */
public interface ApplyState {

    /**
     * 狀態變化處理操作
     */
    void changeHandle();

}

待提交狀態

/**
 * 未稽核狀態(待提交稽核)
 */
public class UnAudited implements ApplyState {
    /**
     * 狀態變化處理操作
     */
    @Override
    public void changeHandle() {
        System.out.println("申請單處於未稽核狀態,當用戶檢視申請單詳情時直接跳轉到編輯頁。");
    }
}

稽核中狀態

/**
 * 稽核中狀態
 */
public class Audit implements ApplyState {
    /**
     * 狀態變化處理操作
     */
    @Override
    public void changeHandle() {
        System.out.println("申請單處於稽核中狀態,當用戶檢視申請單詳情時跳轉到詳情頁可以看到提交記錄。");
    }
}

稽核通過狀態

/**
 *
 * 稽核通過狀態
 */
public class Pass implements ApplyState {
    /**
     * 狀態變化處理操作
     */
    @Override
    public void changeHandle() {
        System.out.println("申請單已經審批通過,當前使用者可以正常休假了。");
    }
}

稽核未通過狀態

/**
 * 稽核未通過狀態
 */
public class NotPass implements ApplyState {
    /**
     * 狀態變化處理操作
     */
    @Override
    public void changeHandle() {
        System.out.println("申請單未通過稽核,當前使用者不可以休假");
    }
}

測試類

public class TestOA {

    public static void main(String[] args) {

        //建立一個請假申請單
        LeaveApply leaveApply = new LeaveApply();

        leaveApply.setState(new UnAudited());
        leaveApply.update();

        leaveApply.setState(new Audit());
        leaveApply.update();

        leaveApply.setState(new Pass());
        leaveApply.update();

        leaveApply.setState(new NotPass());
        leaveApply.update();


    }
}

執行結果:

申請單處於未稽核狀態,當用戶檢視申請單詳情時直接跳轉到編輯頁。
申請單處於稽核中狀態,當用戶檢視申請單詳情時跳轉到詳情頁可以看到提交記錄。
申請單已經審批通過,當前使用者可以正常休假了。
申請單未通過稽核,當前使用者不可以休假

這個例子如果是不使用狀態模式的思想,而是使用條件語句來實現就會出現很多的if-else來進行判斷什麼狀態,應該執行什麼樣的方法。現在把各個狀態的處理邏輯分離,結構清晰了並且耦合也不那麼緊密了。

結構分析

在狀態模式中引入了抽象狀態類和具體狀態類,它們是狀態模式的核心。狀態模式的結構組成如下圖:

在狀態模式中,主要涉及瞭如下幾個角色。

環境角色(Context):定義客戶端所感興趣的介面,並且保留一個具體狀態類的例項。這個具體狀態類的例項給出此環境物件的現有狀態。

抽象狀態角色(State):定義一個介面,用以封裝環境(Context)物件的一個特定的狀態所對應的行為。

具體狀態角色(Contract):每一個具體狀態類都實現了環境(Context)的一個狀態所對應的行為。

總結

狀態模式在功能上和策略模式很類似,但是在實現思想上狀態模式是將各個狀態分離解耦的,並且可以將物件的具體行為委託給當前的狀態物件,而策略模式中,策略的選擇是根據Context類中的行為來確定的,也不存在各個狀態的切換。在實際開發中,狀態模式具有較高的使用頻率,在工作流和遊戲開發中狀態模式都得到了廣泛的應用,例如公文狀態的轉換、遊戲中角色的升級等。

主要優點

1、封裝了狀態的轉換規則,在狀態模式中可以將狀態轉換的工作封裝在環境類或具體的狀態類中,可以對狀態轉換碼進行集中管理,而不是分散在一個個的業務中。

2、將所有與某個狀態有關的行為放到一個類中,只需要注入一個不同的狀態物件即可使環境物件擁有不同的行為。

3、允許狀態轉換邏輯與狀態物件合為一體,而不是提供一個巨大的條件語句塊,狀態模式可以讓我們避免使用龐大的條件語句來將業務方法和狀態轉換程式碼交織在一起。

主要缺點

1、狀態模式的使用必然會增加系統中類和物件的個數,導致系統執行開銷增大。

2、狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和程式碼的混亂,增加系統設計的難度。

使用場景

1、物件的行為依賴於它的狀態(如某些屬性值),狀態的改變將導致行為的變化。

2、在程式碼中包含大量與物件狀態有關的條件語句,這些條件語句的出現,會導致程式碼的可維護性和靈活性變差,不能方便地增加和刪除狀態,並且導致客戶類與類庫之間的耦合增強。

這個是我的個人公眾號,文章以後也會同步到公眾號上去,歡迎關注。