狀態模式是一種行爲模式,用於解決系統中複雜的對象狀態轉換以及各個狀態下的封裝等問題。狀態模式是將一個對象的狀態從該對象中分離出來,封裝到專門的狀態類中,使得對象的狀態能夠靈活多變。這樣在客戶端使用時無需關心對象的狀態,能夠實現自身的一致性處理。最近工做有些忙,更新博客慢了。仍是要嚴格要求本身的,抽時間也要堅持學習。 html
狀態模式容許一個對象在其狀態改變時,改變它的行爲,對象看起來彷佛修改了它的類。設計模式
想要在改變自身狀態時改變對象行爲,最直接的方法就是在代碼中將全部可能發生的狀況都考慮到了,而後使用if-else語句來進行相應的選擇。可是這種方法對於複雜的狀態判斷會顯得雜亂無章,容易產生錯誤;並且增長一個新的狀態將會帶來大量的修改。app
流程結構以下圖:ide
此時「可以修改自身」的狀態模式的引入也許是個不錯的主意,將不一樣條件下的行爲封裝在一個類裏,再給這些類一個統一的父類來約束它們。post
就會變成以下圖流程結構:學習
仍是來舉個具體的實例來講明一下吧。同事小王要請假,根據公司規定,請假要先在OA上提請假申請單,而後審批經過後就能夠正常休假了。這個請假申請單大體經歷這麼幾個狀態,未審覈(待提交)、審覈中、審覈經過、審覈未經過,這裏只是粗略的分爲這幾個狀態。下面咱們使用狀態模式來模擬實現一下這個請假申請單的狀態流轉過程。測試
先建立一個休假申請單類url
/** * 休假申請 */ public class LeaveApply { /** * 休假申請單初始狀態是待提交狀態 */ private ApplyState applyState = new UnAudited(); /** * 設置狀態 * @param state */ public void setState(ApplyState state){ applyState = state; } /** * 狀態變化後,更新對象自身的行爲 */ public void update(){ applyState.changeHandle(); } }
審批單狀態接口,聲明統一處理的方法。spa
/** * 審批單狀態接口 */ 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類中的行爲來肯定的,也不存在各個狀態的切換。在實際開發中,狀態模式具備較高的使用頻率,在工做流和遊戲開發中狀態模式都獲得了普遍的應用,例如公文狀態的轉換、遊戲中角色的升級等。
一、封裝了狀態的轉換規則,在狀態模式中能夠將狀態轉換的工做封裝在環境類或具體的狀態類中,能夠對狀態轉換碼進行集中管理,而不是分散在一個個的業務中。
二、將全部與某個狀態有關的行爲放到一個類中,只須要注入一個不一樣的狀態對象便可使環境對象擁有不一樣的行爲。
三、容許狀態轉換邏輯與狀態對象合爲一體,而不是提供一個巨大的條件語句塊,狀態模式可讓咱們避免使用龐大的條件語句來將業務方法和狀態轉換代碼交織在一塊兒。
一、狀態模式的使用必然會增長系統中類和對象的個數,致使系統運行開銷增大。
二、狀態模式的結構與實現都較爲複雜,若是使用不當將致使程序結構和代碼的混亂,增長系統設計的難度。
一、對象的行爲依賴於它的狀態(如某些屬性值),狀態的改變將致使行爲的變化。
二、在代碼中包含大量與對象狀態有關的條件語句,這些條件語句的出現,會致使代碼的可維護性和靈活性變差,不能方便地增長和刪除狀態,而且致使客戶類與類庫之間的耦合加強。
想了解更多的設計模式請查看Java設計模式學習記錄-GoF設計模式概述。
這個是個人我的公衆號,文章之後也會同步到公衆號上去,歡迎關注。