請直接看原文:
設計模式系列
-------------------------------------------------------------------------------------------------------------------------------?
前言
建議在閱讀本文前先閱讀設計模式(十一)策略模式這篇文章,雖說狀態模式和策略模式的結構幾乎是相同的,但是它們所解決的問題是不同的,讀完這兩篇文章你就會有了答案。
1.狀態模式定義
狀態模式定義
定義:當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變了其類。
狀態模式UML圖
在享元模式中有如下角色:
- Context:環境角色,定義客戶端需要的接口,并且負責具體狀態的切換。
- State:抽象狀態角色,可以是抽象類或者接口,負責對象狀態定義,并封裝了環境角色。
- ConcreteState:具體狀態角色,實現抽象角色類,定義了本狀態所要做的事情。
2.簡單實現狀態模式
拿用mp3聽歌來說,mp3有四種基本狀態,分別是開機、關機、上一首歌和下一首歌。如果我們要寫一個對mp3進行控制的類,你可能會這樣寫,如下所示。
public class Mp3Controller {private static final int POWER_ON = 1;private static final int POWER_OFF = 2;private int state = POWER_OFF;public void powerOn() {if (state == POWER_OFF) {System.out.println("開機");}state = POWER_ON;}public void powerOff() {if (state == POWER_ON) {System.out.println("關機");}state = POWER_OFF;}public void preSong() {if (state == POWER_ON) {System.out.println("上一首歌");}}public void nextSong() {if (state == POWER_ON) {System.out.println("下一首歌");}}
}
在powerOn和powerOff方法中我們會將state置為相應的狀態,在preSong和nextSong方法中,首先要判斷當前mp3的state,如果是POWER_OFF,則不做任何處理,寫到這里你可能會覺得實現很簡單啊。那么我再添加些狀態,比如待機狀態、休眠狀態、亮屏狀態等等,順便再添加些功能,比如調大音量、調小音量、降噪等。這樣你實現起來,就會發現你會定義很多種狀態,在功能中可能要用到多個條件語句進行判斷,這會使得代碼變得臃腫。
狀態模式就是為了解決這一問題,將多個條件語句去掉,使得代碼更加清晰,下面來進行實現。
抽象狀態角色
public interface Mp3State {//開機public void powerOn();//關機public void powerOff();//上一首歌曲public void preSong();//下一首歌曲public void nextSong();
}
接口Mp3State中定義了四種功能,接下來我們來實現Mp3State。
具體狀態角色
我們先來實現開機狀態,代碼如下所示。
public class PowerOnState implements Mp3State {@Overridepublic void powerOn() {System.out.println("已開機");}@Overridepublic void powerOff() {System.out.println("關機");}@Overridepublic void preSong() {System.out.println("上一首歌");}@Overridepublic void nextSong() {System.out.println("下一首歌");}
}
比較特殊的是powerOn方法中,打印了“已開機”,因為在PowerOnState 狀態下進行開機操作是多此一舉的。
接著實現關機狀態:
public class PowerOffState implements Mp3State {@Overridepublic void powerOn() {System.out.println("開機");}@Overridepublic void powerOff() {}@Overridepublic void preSong() {}@Overridepublic void nextSong() {}
}
在關機狀態中只實現了powerOn方法,其他的方法都是空實現。
環境角色
public class Context {private Mp3State mp3State;public void setMp3State(Mp3State mp3State){this.mp3State=mp3State;}public void powerOn(){mp3State.powerOn();setMp3State(new PowerOnState());}public void powerOff(){mp3State.powerOff();setMp3State(new PowerOffState());}public void preSong(){mp3State.preSong();}public void nextSong(){mp3State.nextSong();}
}
Context 中定義了setMp3State方法,用來設定狀態,其中powerOn方法中會調用setMp3State方法將狀態置為PowerOffState,同理powerOff中將狀態置為PowerOffState。
客戶端調用
public class Client {public static void main(String[] args){Context context=new Context();context.setMp3State(new PowerOffState());context.preSong();context.powerOn();context.nextSong();context.powerOff();}
}
我們只需要先設定mp3的初始狀態,就可以調用各種功能方法了,不需要再考慮功能和狀態之間的關系。輸出結果為:
開機
下一首歌
關機
雖然這個例子的代碼很簡單,這里還是給出UML圖,如下所示。
3.狀態模式的使用場景和優缺點
優點
- 避免了過多的條件語句,使得結構更清晰,提高代碼的可維護性。
- 每個狀態都是一個子類,方便增加和修改狀態。
- 狀態被放置到類的內部,外部調用不需要知道類的內部如何實現狀態和行為的變換。
缺點
- 完全使用狀態模式,可能會導致子類會過多。
使用場景
- 代碼中包含大量與對象狀態有關的條件語句。
- 對象的行為依賴著狀態,并且行為隨著狀態的改變而改變。