Java 设计模式之状态模式
一、了解狀態模式
1.1 什么是狀態模式
狀態模式允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了它自己的類。
狀態模式將狀態封裝為獨立的類,并將動作委托到代表當前狀態的對象。
1.2 狀態模式組成結構
- 上下文 (Context):用戶對象,擁有(聚合)一個 State 類型的成員,以標識對象的當前狀態。
- 抽象狀態 (State):接口或基類,封裝與 Context 的特定狀態相關的行為。
- 具體狀態 (ConcreteState):接口實現類或子類,實現了一個與 Context 某個狀態相關的行為。
1.3 狀態模式 UML 圖解
1.4 狀態模式與策略模式
如果對策略模式也了解的話,你會發現狀態模式的類圖與策略模式的類圖幾乎是一樣的。它們之間有什么關系嗎?
PS:策略模式具體博文http://blog.csdn.net/codejas/article/details/79077186
雖然狀態模式與策略模式的類圖是一樣的,但是它們兩個的“意圖”是明顯不一樣的。
對于狀態模式而言,我們將一群行為封裝在狀態對象中,context 的行為隨時可委托到那些狀態對象中的一個。隨著時間的流逝,當前狀態在狀態對象集合中游走改變,以反映出 context 內部的狀態,因此,context 的行為也會隨著改變。但是 context 的客戶對于狀態對象的了解程度不多,甚至根本是渾然不知。
而對于策略模式而言,客戶通常主動指定 context 所需要的策略對象是哪一個。策略模式雖然可以讓我們的代碼更有彈性,但是對于某個 context 對象來說,通常都只有一個最適當的策略對象。
1.5 狀態模式應用場景
- 一個對象的行為取決于它的狀態,并且它必須在運行時刻根據狀態改變它的行為。
- 一個操作中含有龐大的多分支結構,并且這些分支決定于對象的狀態。
二、狀態模式具體應用
2.1 問題描述
糖果機:一種新型糖果機的實現控制流程如圖所示,現在需要我們用 Java 語言來實現它。
2.2 問題分析
從上面這個圖中我們可以找出所有的狀態:沒有一塊錢、有一塊錢、售出糖果盒糖果告罄。也就是上圖中的圓圈部分。為了提高程序的擴展性和維護性,我們采用狀態模式來設計代碼。將四個狀態映射為對應的四個類。
我們需要做的事情如下:
2.3 狀態模式設計類圖
2.4 代碼實現
上下文糖果機 CandyMachine 類
package com.jas.state;/*** 糖果機類* @author Jas* @create 2018-01-31 10:35**/ public class CandyMachine {State noCoinState;State hasCoinState;State soldState;State soldOutState;State state = soldOutState;/** 剛開始糖果機中是沒有糖果的,所以 count 為 0 */int count = 0; /*** 通過構造函數實例化所有狀態對象* @param numberCandys 初始化糖果機中的糖果數量*/public CandyMachine(int numberCandys){noCoinState = new NoCoinState(this);hasCoinState = new HasCoinState(this);soldState = new SoldState(this);soldOutState = new SoldOutState(this);this.count = numberCandys;//如果超過 0 顆糖果,就把狀態設為 noCoinStateif(numberCandys > 0){state = noCoinState;}}public void insertCoin(){state.insertCoin();}public void ejectCoin(){state.ejectCoin();}public void turnCrank(){state.turnCrank();state.dispense();}/*** 該方法用于釋放糖果,并將 count 的數量減一*/void releaseBall(){System.out.println("糖果機釋放一個糖果 ...");if (count != 0){count --;}}public State getNoCoinState() {return noCoinState;}public State getHasCoinState() {return hasCoinState;}public State getSoldState() {return soldState;}public State getSoldOutState() {return soldOutState;}public int getCount() {return count;}public void setState(State state){this.state = state;} }抽象狀態 State 接口
package com.jas.state;/*** 所有的狀態接口* @author Jas* @create 2018-01-31 10:28**/ public interface State {/** 投入一個硬幣 */void insertCoin();/** 彈出硬幣 */void ejectCoin();/** 轉動曲柄 */void turnCrank();/** 糖果分發或告罄 */void dispense(); }具體狀態 NoCoinState 類
package com.jas.state;/*** @author Jas* @create 2018-01-31 10:30**/ public class NoCoinState implements State {CandyMachine candyMachine;public NoCoinState(CandyMachine candyMachine){this.candyMachine = candyMachine;}/*** 投入硬幣后,將糖果機置為有一個硬幣的狀態*/@Overridepublic void insertCoin() {System.out.println("你投入了一個硬幣!");candyMachine.setState(candyMachine.getHasCoinState());}@Overridepublic void ejectCoin() {System.out.println("你還沒有投入一個硬幣!");}@Overridepublic void turnCrank() {System.out.println("即使你轉動了曲柄,但是不會出糖果,因為你沒有投入硬幣!");}@Overridepublic void dispense() {System.out.println("你需要先投入一個硬幣!");} }具體狀態 HasCoinState 類
package com.jas.state;/*** @author Jas* @create 2018-01-31 10:39**/ public class HasCoinState implements State {CandyMachine candyMachine;public HasCoinState(CandyMachine candyMachine){this.candyMachine = candyMachine;}@Overridepublic void insertCoin() {System.out.println("你已經投入了一個硬幣,不能再投入硬幣了!");}/*** 在糖果機有一個硬幣的時候,你可以申請退回你的硬幣,并改變糖果機的狀態*/@Overridepublic void ejectCoin() {System.out.println("您的硬幣已退回!");candyMachine.setState(candyMachine.getNoCoinState());}/*** 糖果機有硬幣的時候,你可以轉動曲柄,從而獲得糖果,并改變糖果機的狀態*/@Overridepublic void turnCrank() {System.out.println("你轉動了曲柄,請稍等 ...");candyMachine.setState(candyMachine.getSoldState());}@Overridepublic void dispense() {System.out.println("現在還沒有糖果發放!");} }具體狀態 SoldState 類
package com.jas.state;/*** @author Jas* @create 2018-01-31 10:40**/ public class SoldState implements State {CandyMachine candyMachine;public SoldState(CandyMachine candyMachine){this.candyMachine = candyMachine;}@Overridepublic void insertCoin() {System.out.println("現在不能投入硬幣了,請稍等,馬上為您發放一個糖果!");}@Overridepublic void ejectCoin() {System.out.println("您已經轉動了曲柄,現在不能再退回硬幣了!");}@Overridepublic void turnCrank() {System.out.println("您已經轉動了曲柄一次,多余的轉動是無效的!");}/*** 分發糖果,并根據糖果機中的糖果數量,改變糖果機的狀態*/@Overridepublic void dispense() {candyMachine.releaseBall();//如果糖果機中有糖果,將糖果機置為初始沒有硬幣的狀態if(candyMachine.getCount() > 0){candyMachine.setState(candyMachine.getNoCoinState());}else {System.out.println("糖果已經售賣完了,請改天再來!");candyMachine.setState(candyMachine.getSoldOutState());}} }具體狀態 SoldOutState 類
package com.jas.state;/*** @author Jas* @create 2018-01-31 10:41**/ public class SoldOutState implements State {CandyMachine candyMachine;public SoldOutState(CandyMachine candyMachine){this.candyMachine = candyMachine;}@Overridepublic void insertCoin() {System.out.println("請不要再投入硬幣了,糖果已經賣完了!");}@Overridepublic void ejectCoin() {System.out.println("您沒有投入硬幣,所以現在不能彈出硬幣!");}@Overridepublic void turnCrank() {System.out.println("即使你轉動了曲柄,但還是不會出糖果,因為已經沒有糖果了!");}@Overridepublic void dispense() {System.out.println("糖果已經賣完了,沒有糖果用于發放了!");} }測試類
package com.jas.state;/*** @author Jas* @create 2018-01-31 11:04**/ public class CansyMachineTestDrive {public static void main(String[] args) {//初始化糖果機中的糖果數量為 1 個CandyMachine candyMachine = new CandyMachine(1); candyMachine.insertCoin();candyMachine.ejectCoin();System.out.println("======================");candyMachine.insertCoin();candyMachine.turnCrank();System.out.println("======================");candyMachine.insertCoin();candyMachine.turnCrank();} }/*** 輸出* 你投入了一個硬幣!* 您的硬幣已退回!* ======================* 你投入了一個硬幣!* 你轉動了曲柄,請稍等 ...* 糖果機釋放一個糖果 ...* 糖果已經售賣完了,請改天再來!* ======================* 請不要再投入硬幣了,糖果已經賣完了!* 即使你轉動了曲柄,但還是不會出糖果,因為已經沒有糖果了!* 糖果已經賣完了,沒有糖果用于發放了!*/2.5 問題總結
在上面這個例子中,客戶并不直接與具體狀態類直接交互,而是通過糖果機類向狀態類發出請求,還有要注意的是,客戶并不會直接改變糖果機的狀態。了解狀態的是糖果機類,客戶并不了解,所以客戶不會直接與狀態類交互。在糖果機類中有一個狀態實例,用這個實例來完成狀態之間不同的切換。
三、 狀態模式總結
3.1 狀態模式的優缺點
優點
- 封裝了轉換規則。
- 枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。
- 將所有與某個狀態有關的行為放到一個類中,并且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為。
- 允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。
- 可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。
缺點
3.2 狀態模式知識總結
- 狀態模式允許一個對象基于內部的狀態而擁有不同的行為。
- 狀態模式用類來表示狀態。
- 上下文 (Context) 會將行為委托給當前的狀態對象。
- 狀態模式和策略模式雖然有相同的類圖,但是它們的意圖是不一樣的。
- 狀態模式允許上下文隨著狀態的改變而改變行為。
- 使用狀態模式通常會導致類的數量大量增加。
- 狀態類可以被多個上下文 (Context) 實例共享。
PS:點擊了解更多設計模式 http://blog.csdn.net/codejas/article/details/79236013
參考文獻
《Head First 設計模式》
http://www.runoob.com/design-pattern/state-pattern.html
總結
以上是生活随笔為你收集整理的Java 设计模式之状态模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 经办人房租代开发票哪里看
- 下一篇: Java web 面试题