【趣味設計模式系列】之【狀態模式】

1. 簡介

狀態模式(State Pattern),當一個對象內在狀態改變時容許其改變行爲,這個對象看起來像改變了其類。簡而言之,就是狀態的變動引發了行爲的變動數據庫

2. 圖解

下圖四輛汽車,分別表明汽車平常的四種狀態。
開門狀態:
this

關門狀態:
設計

飛奔狀態:
3d

中止狀態:
code

其中,某種特定狀態下,都有四個可執行操做,分別是open,close,run,stop,而後作對應的處理得下圖所示。
對象

3. 案例實現

類圖以下:
blog

  • 定義汽車抽象狀態類CarState,持有類型爲Context的屬性,同時持有四個可執行操做,opencloserunstop方法;
  • 定義汽車抽象狀態類的子類OpenningStateClosingStateRunningStateStoppingState,分別表明開門狀態,關門狀態,飛奔狀態,中止狀態;
  • 定義環境角色類Context,把狀態對象聲明爲靜態常量,有幾個狀態對象就聲明幾個靜態常量,環境角色具備狀態抽象角色定義的全部行爲,具體執行使用委託方式。具體環境角色有兩個職責:處理本狀態必須完成的任務,決定是否能夠過渡到其餘狀態。

代碼實現以下:get

package com.wzj.state.example1;

/**
 * @Author: wzj
 * @Date: 2019/11/3 20:10
 * @Desc: 汽車狀態抽象類
 */
public abstract class CarState {
    //環境角色,封裝狀態變化引發的行爲變化
    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }

    //汽車開門動做
    public abstract void open();

    //汽車關門動做
    public abstract void close();

    //汽車飛奔動做
    public abstract void run();

    //汽車中止動做
    public abstract void stop();

}
package com.wzj.state.example1;

/**
 * @Author: wzj
 * @Date: 2019/11/3 20:23
 * @Desc: 汽車開門狀態類
 */
public class OpenningState extends CarState {

    //打開汽車門
    public void open() {
        System.out.println("汽車門已開");
    }

    //關閉汽車門
    public void close() {
        //狀態修改
        super.context.setCarState(Context.closingState);
        //動做委託爲ClosingState來執行
        super.context.getCarState().close();
    }

    //門開着時汽車通常不奔跑
    public void run() {
        System.out.println("汽車開門狀態,不能奔跑");
    }

    //車門開着時,切換不到中止狀態,由於沒有四種狀態中,沒有開門且中止這個狀態
    public void stop() {
        System.out.println("汽車開門狀態,不能長時間開着門且處於中止狀態");

    }
}
package com.wzj.state.example1;

/**
 * @Author: wzj
 * @Date: 2019/11/3 20:23
 * @Desc: 汽車飛奔狀態類
 */
public class RunningState extends CarState {

    //打開奔跑時不開門
    public void open() {
        System.out.println("車在飛奔,不能打開");
    }

    //奔跑時確定是關門的
    public void close() {
        System.out.println("車在飛奔,已經關閉,不能再次關閉");
    }

    //汽車在飛奔
    public void run() {
        System.out.println("汽車在飛奔");
    }

    //汽車能夠停下來
    public void stop() {
        //修改汽車爲中止狀態
        super.context.setCarState(Context.stoppingState);
        //中止動做委託爲StoppingState類來執行
        super.context.getCarState().stop();
    }
}
package com.wzj.state.example1;

/**
 * @Author: wzj
 * @Date: 2019/11/3 20:23
 * @Desc: 汽車關門狀態類
 */
public class ClosingState extends CarState {

    //打開汽車門
    public void open() {
        //修改汽車爲開門狀態
        super.context.setCarState(Context.openningState);
        //動做委託爲OpenningState類來執行
        super.context.getCarState().open();
    }

    //關閉汽車門
    public void close() {
        System.out.println("汽車門已關");
    }

    //汽車在飛奔
    public void run() {
        //修改汽車爲飛奔狀態
        super.context.setCarState(Context.runningState);
        //動做委託爲RunningState類來執行
        super.context.getCarState().run();

    }

    //汽車在中止
    public void stop() {
        //設置汽車狀態爲中止狀態
        super.context.setCarState(Context.stoppingState);
        //動做委託爲StoppingState類來執行
        super.context.getCarState().stop();

    }
}
package com.wzj.state.example1;

/**
 * @Author: wzj
 * @Date: 2019/11/3 20:19
 * @Desc: 上下文環境類
 */
public class Context {

    /**列出汽車全部狀態
     * openningState-開門狀態 closingState-關門狀態
     * runningState-奔馳狀態 stoppingState-中止狀態
     */
    public static final OpenningState openningState = new OpenningState();
    public static final ClosingState closingState = new ClosingState();
    public static final RunningState runningState = new RunningState();
    public static final StoppingState stoppingState = new StoppingState();

    //定義汽車當前狀態
    private CarState carState;

    public CarState getCarState() {
        return  carState;
    }

    public void setCarState(CarState carState) {
        this.carState = carState;
        //切換狀態
        this.carState.setContext(this);
    }

    //汽車開門
    public void open() {
        this.carState.open();
    }

    //汽車關門
    public void close(){
        this.carState.close();
    }

    //汽車飛奔
    public void run(){
        this.carState.run();
    }

    //汽車中止
    public void stop(){
        this.carState.stop();
    }

}

客戶端類以下:it

package com.wzj.state.example1;

/**
 * @Author: wzj
 * @Date: 2019/11/3 21:06
 * @Desc:
 */
public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        context.setCarState(new OpenningState());
//        context.setCarState(new ClosingState());
//        context.setCarState(new RunningState());
//        context.setCarState(new StoppingState());
        context.open();
//        context.close();
//        context.run();
//        context.stop();
    }
}

執行結果以下:
當只打開Client15行的時候,分別打開11,12,13,14行的代碼,會獲得以下結果:
汽車爲開門狀態時,執行open
class

汽車爲關門狀態時,執行open

汽車爲飛奔狀態時,執行open

汽車爲中止狀態時,執行open

上述結果能夠看出,一樣執行一個open方法,當狀態的變化時致使行爲的變化。

4. 狀態模式總結

優勢

  • 結構清晰
    避免了過多的switch...case或者if...else語句的使用,避免了程序的複雜性,提升系統的可維護性;
  • 遵循設計原則
    很好地體現了開閉原則和單一職責原則,每一個狀態都是一個子類,你要增長狀態就要增長子類,你要修改狀態,你只修改一個子類就能夠了。
  • 封裝性很是好
    這也是狀態模式的基本要求,狀態變換放置到類的內部來實現,外部的調用不用知道類內部如何實現狀態和行爲的變換。
    缺點 狀態模式既然有優勢,那固然有缺點了。但只有一個缺點,子類會太多,也就是類膨脹。若是一個事物有不少個狀態也不稀奇,若是徹底使用狀態模式就會有太多的子類,很差管理,這個須要你們在項目中本身衡量。其實有不少方式能夠解決這個狀態問題,如在數據庫中創建一個狀態表,而後根據狀態執行相應的操做,這個也不復雜,看你們的習慣和嗜好了。
相關文章
相關標籤/搜索