前言
建議在閱讀本文前先閱讀設計模式(十一)策略模式這篇文章,雖說狀態模式和策略模式的結構幾乎是相同的,但是它們所解決的問題是不同的,讀完這兩篇文章你就會有了答案。
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 {
@Override
public void powerOn() {
System.out.println("已開機");
}
@Override
public void powerOff() {
System.out.println("關機");
}
@Override
public void preSong() {
System.out.println("上一首歌");
}
@Override
public void nextSong() {
System.out.println("下一首歌");
}
}複製程式碼
比較特殊的是powerOn方法中,列印了“已開機”,因為在PowerOnState 狀態下進行開機操作是多此一舉的。
接著實現關機狀態:
public class PowerOffState implements Mp3State {
@Override
public void powerOn() {
System.out.println("開機");
}
@Override
public void powerOff() {
}
@Override
public void preSong() {
}
@Override
public 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.狀態模式的使用場景和優缺點
優點
- 避免了過多的條件語句,使得結構更清晰,提高程式碼的可維護性。
- 每個狀態都是一個子類,方便增加和修改狀態。
- 狀態被放置到類的內部,外部呼叫不需要知道類的內部如何實現狀態和行為的變換。
缺點
- 完全使用狀態模式,可能會導致子類會過多。
使用場景
- 程式碼中包含大量與物件狀態有關的條件語句。
- 物件的行為依賴著狀態,並且行為隨著狀態的改變而改變。
歡迎關注我的微信公眾號,第一時間獲得部落格更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,即可關注。