概念:狀態模式把所研究的對象的行為包裝在不同的狀態對象裡,每一個狀態對象都屬於一個抽象狀態類的一個子類。狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行為也隨之改變,也就是不同狀態對應不同的行為。狀態模式的示意性類圖如下所示:
UML類圖:Context:可以理解成控制類
State是狀態接口
ConcreteStateA和ConcreteStateB可以認為是接口的實現,也就是具體的狀態實現類。
使用場景:對象的狀態決定對象的行為,在運行時根據狀態動態調整對象的行為。
代碼中有復雜的if else判斷,且這些分支判斷和狀態有關系。
實際場景:(1)電視開關機狀態,電視開機狀態下才可以進行各種操作,關機狀態下,不能進行各項操作。
(2)WIFI狀態,WIFI開狀態下可以連接WIFI,關閉狀態不可進行操作。
(3)登錄狀態,這個在開發中較為常用,一般在進入系統實現分享轉發等操作時要先判斷用戶的登錄狀態,若已經登錄則可進行操作,否則提示用戶登錄。
我們來實現場景1.
首先創建一個狀態接口(對應UML類圖中的State接口):
package com.state.demo; public interface TvState { /** * 電視狀態的接口,裡面提供四種方法 */ void nextChanel(); void preChanel(); void turnUp(); void turnDown(); }
接下來兩個實現類(對應UML類圖中ConcreteStateA):
package com.state.demo; public class PowerOnState implements TvState { /** * 開機狀態下,遙控器按鈕有效 */ @Override public void nextChanel() { System.out.println("---------下一頻道-----------------------"); } @Override public void preChanel() { System.out.println("---------上一頻道-----------------------"); } @Override public void turnUp() { System.out.println("---------音量增大-----------------------"); } @Override public void turnDown() { System.out.println("---------音量減小-----------------------"); } }
實現類2(對應UML類圖中ConcreteStateB):
package com.state.demo; public class PowerOffState implements TvState { /** * 關機狀態下,所有按鈕無效 */ @Override public void nextChanel() { System.out.println("---------關機狀態不可用,請先開機-----------------------"); } @Override public void preChanel() { System.out.println("---------關機狀態不可用,請先開機-----------------------"); } @Override public void turnUp() { System.out.println("---------關機狀態不可用,請先開機-----------------------"); } @Override public void turnDown() { System.out.println("---------關機狀態不可用,請先開機-----------------------"); } }
接下來控制類(對應UML類圖中Context):
package com.state.demo; public class TvController { TvState tvState=null; public void setTvState(TvState tvState) { this.tvState = tvState; } /** * 開機 */ public void turnOnTv(){ setTvState(new PowerOnState()); System.out.println("--------開機啦---------------"); } /** * 關機 */ public void turnOffTv(){ setTvState(new PowerOffState()); System.out.println("--------關機啦---------------"); } /** * 下一頻道 */ public void nextChanel(){ tvState.nextChanel(); } public void preChanel(){ tvState.preChanel(); } public void turnUp(){ tvState.turnUp(); } public void turnDown(){ tvState.turnDown(); } }
最後,編寫一個測試類,測試以上代碼:
package com.state.demo; public class TestClass { public static void main(String[] args) { TvController tvController=new TvController();//創建一個控制類 tvController.turnOnTv();//首先開機 tvController.nextChanel(); tvController.turnDown(); tvController.turnOffTv();//關機 tvController.turnDown();//關機後功能不再提供 } }
運行實例如下:
這裡有些邏輯小問題,就是在已經開機的狀態下,用戶再次調用開機要進行提示,這時我們可以在控制類中加入如下代碼;
package com.state.demo; public class TvController { private boolean isTvOn=false; TvState tvState=null; public void setTvState(TvState tvState) { this.tvState = tvState; } /** * 開機 */ public void turnOnTv(){ if(!isTvOn){ isTvOn=true; setTvState(new PowerOnState()); System.out.println("--------開機啦---------------"); }else{ isTvOn=true; System.out.println("--------已經開機了,不要再按了---------------"); } } /** * 關機 */ public void turnOffTv(){ if(isTvOn){ isTvOn=false; setTvState(new PowerOffState()); System.out.println("--------關機啦---------------"); }else{ isTvOn=false; System.out.println("-------已經關機啦,不要再按了---------------"); } } /** * 下一頻道 */ public void nextChanel(){ tvState.nextChanel(); } public void preChanel(){ tvState.preChanel(); } public void turnUp(){ tvState.turnUp(); } public void turnDown(){ tvState.turnDown(); } }
做一個開機標識,每次調用方法之前進行判斷即可。這時再次運行測試類:
package com.state.demo; public class TestClass { public static void main(String[] args) { TvController tvController=new TvController();//創建一個控制類 tvController.turnOnTv();//首先開機 tvController.turnOnTv();//首先開機 tvController.nextChanel(); tvController.turnDown(); tvController.turnOffTv();//關機 tvController.turnOffTv();//關機 tvController.turnDown();//關機後功能不再提供 } }
運行如下:
總結:控制類持有系統狀態,但控制類不直接處理行為,行為在狀態的實現類中實現;
用戶直接操作控制類,直接和控制類交互,不直接操作狀態實現類,這樣就有一個職責的分離,有利於系統維護。
喜歡的朋友請關注我,謝謝。