設計模式-狀態模式(State Pattern)

qq_34359363發表於2018-09-05

狀態模式是什麼?

允許一個物件再其類別狀態發生改變時改變它的行為,也就是說類的行為是基於它的狀態改變的。

狀態模式的要素

  1. 使用環境(context)角色:客戶程式是通過它來滿足自己的需求,它定義了客戶程式需要的介面,並且維護一個具體狀態角色例項。通過這個例項來決定當前的狀態。
  2. 狀態(state)角色:定義一個介面以封裝與使用環境角色的一個特定狀態相關的行為。

具體狀態角色(Concrete State):實現狀態角色定義的介面。

狀態模式示意圖

狀態模式適用的場景

 1、行為隨狀態改變而改變的場景。 2、條件、分支語句的代替者(if—else,switch-case)。

簡單例子

package myDesignPatternsTest.statePattern.simpleDemo;

public class Context {
    private  State state;
    public Context() {
        state=null;
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

}
package myDesignPatternsTest.statePattern.simpleDemo;

public interface State {
    public void doAction(Context context);
}
package myDesignPatternsTest.statePattern.simpleDemo;

public class RunState implements  State {
    @Override
    public void doAction(Context context) {
        System.out.println("Player is in Run state");
        context.setState(this);

    }

    @Override
    public String toString() {
        return "RunState{}";
    }
}
package myDesignPatternsTest.statePattern.simpleDemo;

public class StopState implements State {
    @Override
    public void doAction(Context context) {
        System.out.println("is stop state");
        context.setState(this);
    }

    @Override
    public String toString() {
        return "stop";
    }
}
package myDesignPatternsTest.statePattern.simpleDemo;

public class Client {
    public static void main(String[] args) {
        Context context=new Context();
        RunState runState=new RunState();
        runState.doAction(context);
        System.out.println(context.getState());
        StopState stopState=new StopState();
        stopState.doAction(context);
        System.out.println(context.getState());
    }
}

這個簡單的例子沒什麼多講的。

複雜一點的例子

我們每天電梯執行的幾種狀態:開門,關門,執行,停止。

 開門的時候:可以關門和停止

 關門的時候:可以開門,執行,停止

 執行的時候:只能停止

 停止的時候:開門和執行

package myDesignPatternsTest.statePattern.diffDemo;

/**
 * 定義電梯的狀態
 */
public abstract class LiftState {
    //定義一個環境角色,也就是封裝狀態的變換引起的功能變化
    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 myDesignPatternsTest.statePattern.diffDemo;

public class Context {
    //定義出所有電梯的狀態
    public static final OpenningState OPENNING_STATE=new OpenningState();
    public static final ClosingState  CLOSING_STATE=new ClosingState();
    public static final RunningState  RUNNING_STATE=new RunningState();
    public static final StoppingState STOPPING_STATE=new StoppingState();


    //定義一個當前電梯的狀態
    private LiftState state;

    public LiftState getState() {
        return state;
    }

    public void setState(LiftState state) {
        this.state = state;
        //把當前電梯的狀態通知到各個實現類
        this.state.setContext(this);
    }

    public void open(){
        this.state.open();
    }
    public void close(){
        this.state.close();
    }
    public void run(){
        this.state.run();
    }
    public void stop() {
        this.state.stop();

    }


    }
package myDesignPatternsTest.statePattern.diffDemo;

public class ClosingState extends  LiftState {
    //這個是關閉狀態該有的動作
    public void close() {
        System.out.println("電梯門關閉...");
    }

    //關閉後電梯是可以開門的
    public void open() {
        super.context.setState(Context.OPENNING_STATE);
        super.context.open();
    }

    //關門後電梯是可以爬樓的
    public void run() {
     super.context.setState(Context.RUNNING_STATE);
     super.context.run();
    }

    //關門了電梯也可能是停止執行了
    public void stop() {
        super.context.setState(Context.STOPPING_STATE);
        super.context.stop();
    }
}
package myDesignPatternsTest.statePattern.diffDemo;

public class OpenningState extends LiftState {

    @Override
    public void open() {
        System.out.println("電梯的門是開著的");

    }

    //開了當然也能關了
    public void close() {
        super.context.setState(Context.CLOSING_STATE);
        super.context.close();
    }

    //不可能開著跑
    public void run() {
        System.out.println("error:此人有病開著門跑");

    }

    //開門是時候一定是停止的
    public void stop() {

    }
}
package myDesignPatternsTest.statePattern.diffDemo;

public class RunningState extends  LiftState{
    //正在執行的時候開啟門?不存在
    public void open() {

    }

    //跑的時候門一定是關閉的
    public void close() {

    }

    @Override
    public void run() {
        System.out.println("電梯正在爬樓");
    }

    //能跑就能停
    public void stop() {
     super.context.setState(Context.STOPPING_STATE);
     super.context.stop();
    }
}
package myDesignPatternsTest.statePattern.diffDemo;

public class StoppingState extends  LiftState {
    @Override
    public void open() {
        super.context.setState(Context.OPENNING_STATE);
        super.context.open();
    }

    //停止的時候門應該是關的
    public void close() {

    }

    @Override
    public void run() {
        super.context.setState(Context.RUNNING_STATE);
        super.context.run();
    }


    public void stop() {
        System.out.println("現在電梯停止了");

    }
}
package myDesignPatternsTest.statePattern.diffDemo;

public class Client {
    public static void main(String[] args) {
        Context context= new Context();
        context.setState(new StoppingState());
        context.open();
        context.close();
        context.run();

        context.close();
        context.stop();
    }


}
電梯的門是開著的
電梯門關閉...
電梯正在爬樓
現在電梯停止了

Process finished with exit code 0

狀態模式的優缺點

優點: 1、封裝了轉換規則。 2、列舉可能的狀態,在列舉狀態之前需要確定狀態種類。 3、將所有與某個狀態有關的行為放到一個類中,並且可以方便地增加新的狀態,只需要改變物件狀態即可改變物件的行為。 4、允許狀態轉換邏輯與狀態物件合成一體,而不是某一個巨大的條件語句塊。 5、可以讓多個環境物件共享一個狀態物件,從而減少系統中物件的個數。

缺點: 1、狀態模式的使用必然會增加系統類和物件的個數。 2、狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和程式碼的混亂。 3、狀態模式對"開閉原則"的支援並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的原始碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的原始碼。

相關文章