Java設計模式筆記之狀態模式

不同的時間人會處於不同的狀態中

放在程序的世界中就是不同的時間下做出判斷來實現。


1.定義

狀態模式(State):當一個對象的內在狀態改變時允許改變其行爲,這個對象看起來像是改變了其類。


2.使用場景

狀態模式主要解決的是當控制一個對象狀態轉換的條件表達式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把複雜的判斷邏輯簡化。

什麼時候可以使用狀態模式?

當一個對象的行爲取決於它的狀態,並且它必須在在運行時刻根據狀態改變它的行爲時,就可以考慮使用狀態模式。

3.UML圖



4.簡單實例

電視遙控器爲例來演示狀態模式,電視有關機狀態和開機狀態。開機狀態下可以有的操作有下一個頻道,上一個頻道,加音量和減音量等。

/**
 * version 1.0
 * 電視遙控器,含有開機、關機、下一個頻道、上一個頻道、調高音量、調低音量這幾個功能
 */

public class TvController {

    //開機狀態
    private static final int POWER_ON = 1;
    //關機狀態
    private static final int POWER_OFF = 2;
    private int mState = POWER_OFF;

    public void powerOn(){
        mState = POWER_ON;
        if (mState == POWER_OFF){
            Log.i("zsf","開機啦!");
        }
    }

    public void powerOff(){
        mState = POWER_OFF;
        if (mState == POWER_ON){
            Log.i("zsf","關機啦!");
        }
    }

    public void nextChannel(){
        if (mState == POWER_ON){
            Log.i("zsf","下一個頻道");
        } else {
            Log.i("zsf","兩個紅燈提示沒有開機");
        }
    }

    public void prevChannel(){
        if (mState == POWER_ON){
            Log.i("zsf","上一個頻道");
        } else {
            Log.i("zsf","兩個紅燈提示沒有開機");
        }
    }

    public void turnUp(){
        if (mState == POWER_ON){
            Log.i("zsf","提高音量");
        } else {
            Log.i("zsf","兩個紅燈提示沒有開機");
        }
    }

    public void turnDown(){
        if (mState == POWER_ON){
            Log.i("zsf","減小音量");
        } else {
            Log.i("zsf","兩個紅燈提示沒有開機");
        }
    }

}

上面的版本1.0中我們可以看到很明顯的存在很多的if-else語句,這些代碼都是重複的,而且一旦我們的狀態增多,功能增多,我們這個類就要增多很多的函數,那麼維護這個類的代價也將隨之增大。

改爲使用狀態模式,將這些狀態用對象來代替,將這些行爲封裝到對象中去,使得在不同的狀態下有不同的實現。下面是新的代碼實現:

/**
 * 電視狀態接口,定義了電視操作的函數
 */

public interface TvState {

    void nextChannel();
    void prevChannel();
    void turnUp();
    void turnDown();

}


/**
 * 關機狀態,此時只有開機功能是有效的
 */

public class PowerOffState implements TvState {
    @Override
    public void nextChannel() {

    }

    @Override
    public void prevChannel() {

    }

    @Override
    public void turnUp() {

    }

    @Override
    public void turnDown() {

    }
}


/**
 * 開機狀態,此時再觸發開機功能不做任何操作
 */

public class PowerOnState implements TvState {
    @Override
    public void nextChannel() {
        Log.i("zsf","下一個頻道");
    }

    @Override
    public void prevChannel() {
        Log.i("zsf","上一個頻道");
    }

    @Override
    public void turnUp() {
        Log.i("zsf","增加音量");
    }

    @Override
    public void turnDown() {
        Log.i("zsf","減小音量");
    }
}


/**
 * 電源操作接口
 */

public interface PowerController {
    void powerOn();
    void powerOff();
}


/**
 * 電視遙控器,類似於經典狀態模式中的Context
 */

public class NewTvController implements PowerController {
    TvState mTvState;
    public void setTvState(TvState tvState){
        this.mTvState = tvState;
    }


    @Override
    public void powerOn() {
        setTvState(new PowerOnState());
        Log.i("zsf","開機啦");
    }

    @Override
    public void powerOff() {
        setTvState(new PowerOffState());
        Log.i("zsf","關機啦");
    }

    public void nextChannel(){
        mTvState.nextChannel();
    }

    public void prevChannel(){
        mTvState.prevChannel();
    }

    public void turnUp(){
        mTvState.turnUp();
    }

    public void turnDown(){
        mTvState.turnDown();
    }
}



/**
 * 客戶端代碼
 */

public class ClientActivity extends Activity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);//只是新建了個佈局文件,裏面沒有添加任何控件

        NewTvController controller = new NewTvController();
        //設置開機狀態
        controller.powerOn();
        //下一個頻道
        controller.nextChannel();
        //上一個頻道
        controller.prevChannel();
        //調高音量
        controller.turnUp();
        //減小音量
        controller.turnDown();
        //設置關機狀態
        controller.powerOff();
        //再調低音量(此時沒有效果的)
        controller.turnDown();

    }
}


Log日誌

04-30 00:29:57.947 18055-18055/? I/zsf: 開機啦
04-30 00:29:57.947 18055-18055/? I/zsf: 下一個頻道
04-30 00:29:57.948 18055-18055/? I/zsf: 上一個頻道
04-30 00:29:57.948 18055-18055/? I/zsf: 增加音量
04-30 00:29:57.948 18055-18055/? I/zsf: 減小音量
04-30 00:29:57.948 18055-18055/? I/zsf: 關機啦

我們可以看到下圖中紅色框內的Log消息沒有打印出來


對比之前的一個class,我們需要的類或接口多了,但是代碼的維護和可擴展性增強了。

發佈了105 篇原創文章 · 獲贊 35 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章