設計模式之禪——狀態模式


我們每個人都乘過電梯,電梯的動作:開門、關門、運行、停止。

現在我們用程序來實現一個電梯的動作,先看類圖設計,如圖所示

這里寫圖片描述

現在看一下代碼

public interface ILift {
//開啟電梯
public void open();
//關閉電梯
public void close();
//能運行,能上能下
public void run();
//電梯還要能停下來i
public void stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class Lift implements ILift {    @Override    public void open() {        System.out.println("電梯門開啟...");    }    @Override    public void close() {        System.out.println("電梯門關閉...");    }    @Override    public void run() {        System.out.println("電梯上下運行起來...");    }    @Override    public void stop() {        System.out.println("電梯停止了...");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
public class Client {    public static void main(String[] args) {        ILift lift = new Lift();        //首先是電梯門打開,人進去        lift.open();        //r然后電梯門關閉        lift.close();        //電梯開始運行起來,向上或者向下        lift.run();        //最后到達目的地,電梯停下來        lift.stop();    }}/**Output電梯門開啟...電梯門關閉...電梯上下運行起來...電梯停止了...*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

代碼非常簡單,但是這個程序是有問題的。

———————————————————— 
1、電梯門可以打開,但不是隨時都可以打開,電梯運行的時候不能突然開門。

2、電梯也不會出現停止了但是不開門的情況。 
————————————————————

所以我們可以看出,電梯的這四個動作的執行都有前置條件,具體點說就是在特定狀態下才能做做特定的事。

接下來我們分析一下電梯有哪些特定的狀態

  • 敞門狀態 
    按了電梯上下按鈕,電梯門開,這中間大概有10秒的時間,那就是敞門狀態。在這個狀態下電梯只能做的動作就是關門動作。

  • 閉門狀態 
    電梯門關閉了,在這個狀態下,可以進行的動作是:開門(我不想坐電梯了)、停止(忘記按路層號了)、運行。

  • 運行狀態 
    電梯正在跑,上下竄,在這個狀態下,電梯能做的是停止
  • 停止狀態 
    電梯停止不動,在這個狀態下,電梯有兩個可選動作:繼續運行和開門動作。

現在重新進行設計,類圖如下: 
這里寫圖片描述

————————————————————————————— 
在接口中添加了標識變量,類LIft中的open、close等方法帶有了邏輯判斷。 
—————————————————————————————

代碼如下:

public interface ILift {
//電梯的4個狀態
final static int OPENING_STATE = 1;
final static int CLOSING_STATE = 2;
final static int RUNNING_STATE = 3;
final static int STOPPING_STATE = 4;
//開啟電梯
public void open();
//關閉電梯
public void close();
//能運行,能上能下
public void run();
//電梯還要能停下來i
public void stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
public class Lift implements ILift {    private int state;    public void setState(int state) {        this.state = state;    }    @Override    public void open() {       //電梯在什么狀態下才能開啟        switch (this.state) {            case OPENING_STATE:                break;            case CLOSING_STATE:                this.openWithoutLogic();                this.setState(OPENING_STATE);                break;            case RUNNING_STATE:                break;            case STOPPING_STATE:                this.openWithoutLogic();                this.setState(OPENING_STATE);                break;        }    }    @Override    public void close() {        //電梯在什么狀態下才能關閉        switch (this.state) {            case OPENING_STATE:                this.closeWithoutLogic();                this.setState(CLOSING_STATE);                break;            case CLOSING_STATE:                break;            case RUNNING_STATE:                break;            case STOPPING_STATE:                break;        }    }    @Override    public void run() {        switch (this.state) {            case OPENING_STATE:                break;            case CLOSING_STATE:                this.runWithoutLogic();                this.setState(RUNNING_STATE);                break;            case RUNNING_STATE:                break;            case STOPPING_STATE:                this.runWithoutLogic();                this.setState(RUNNING_STATE);                break;        }    }    @Override    public void stop() {        switch (this.state) {            case OPENING_STATE:                break;            case CLOSING_STATE:                this.stopWithoutLogic();                this.setState(STOPPING_STATE);                break;            case RUNNING_STATE:                this.runWithoutLogic();                this.setState(STOPPING_STATE);                break;            case STOPPING_STATE:                break;        }    }    public void closeWithoutLogic(){        System.out.println("電梯門關閉...");    }    public void openWithoutLogic(){        System.out.println("電梯門開啟...");    }    public void runWithoutLogic(){        System.out.println("電梯上下運行起來...");    }    public void stopWithoutLogic(){        System.out.println("電梯停止了...");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
public class Client {    public static void main(String[] args) {        ILift lift = new Lift();        lift.setState(OPENING_STATE);        //首先是電梯門打開,人進去        lift.open();        //r然后電梯門關閉        lift.close();        //電梯開始運行起來,向上或者向下        lift.run();        //最后到達目的地,電梯停下來        lift.stop();    }}/**Output電梯門開啟...電梯門關閉...電梯上下運行起來...電梯停止了...*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

好了,改過的代碼已經實現了簡單的邏輯控制。但是它好像還是有不少問題。 
————————————————————————————

  • 電梯實現類Lift有點長 
    -長的原因是因為我們在程序中使用了大量的switch…case這樣的判斷(if…else也是一樣)

  • 拓展類非常差,電梯還有兩個狀態,通電狀態,斷電狀態,你要是在程序增加這兩個方法,你看看Open()、Close()、Run()、Stop()這四個方法都要增加判斷條件,也就是說在switch判斷體中還要增加case項、這與開閉原則相違背。

  • 非常規狀態無法實現 
    考慮電梯故障等情況就更麻煩了…

————————————————————————————— 
我們已經發現程序中有以上問題,我們怎么來修改呢? 
剛剛我們是從電梯的方法以及這些方法執行的條件去分析,現在我們換個角度來看問題。我們來想,電梯在具有這些狀態的時候能夠做什么實行,也就是說電梯在處於某個具體狀態時,我們來思考 這個狀態是由什么動作出發觸發的而產生的,以及在這個狀態下電梯還能做什么事情。例如,電器在停止狀態時,我們來思考兩個問題: 
————————————————————————————— 
- 停止狀態是怎么來的,那當然是由於電梯執行了stop方法而來的。 

- 在停止狀態下,電梯還能做什么動作?繼續運行?開門?當然都可以了

————————————————————————————— 
我們再來分析其他三個狀態,也都是一樣的結果,我們只要實現電梯在一個狀態下的兩個任務模型就可以了:這個狀態是如何產生的,以及在這個狀態下還能做什么其他動作(也就是這個狀態怎么過度到其他狀態),既然我們以狀態作為參考模型,那我們先定義電梯的狀態接口,類圖如下 
這里寫圖片描述

代碼如下

public abstract class LiftState {
//定義一個環境角色,也就是封裝狀態的變化引起的功能變化
protected Context context = new 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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
public class Context {    //定義出所有的電梯狀態    public final static OpenningState openningState =            new OpenningState();    public final static ClosingState closingState =            new ClosingState();    public final static RunningState runningState =            new RunningState();    public final static StoppingState stoppingState =            new StoppingState();    //定義一個當前的電梯狀態    private LiftState liftState;    public LiftState getLiftState() {        return liftState;    }    public void setLiftState(LiftState liftState) {        this.liftState = liftState;       // this.liftState.stop();    }    public void open() {        this.liftState.open();    }    public void close() {        this.liftState.close();    }    public void run() {        this.liftState.run();    }    public void stop() {        this.liftState.stop();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
public class ClosingState extends LiftState {    //電梯門關閉,這是關閉狀態要實現的動作    @Override    public void close() {        System.out.println("電梯門關閉...");    }    //電梯門關了再打開    @Override    public void open() {        super.context.setLiftState(Context.openningState);        super.context.getLiftState().open();    }    @Override    public void run() {        super.context.setLiftState(Context.runningState);        super.context.getLiftState().run();    }    @Override    public void stop() {        super.context.setLiftState(Context.stoppingState);        super.context.getLiftState().stop();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
public class OpenningState extends LiftState {    @Override    public void open() {        System.out.println("電梯門開啟...");    }    @Override    public void close() {        //狀態修改        super.context.setLiftState(Context.closingState);        super.context.getLiftState().close();    }    @Override    public void run() {    }    @Override    public void stop() {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
public class RunningState extends LiftState {    @Override    public void open() {    }    @Override    public void close() {    }    @Override    public void run() {        System.out.println("電梯門上下運行...");    }    @Override    public void stop() {        super.context.setLiftState(Context.stoppingState);        super.context.getLiftState().stop();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
public class StoppingState extends LiftState {    @Override    public void open() {        super.context.setLiftState(Context.openningState);        super.context.getLiftState().open();    }    @Override    public void close() {    }    @Override    public void run() {        super.context.setLiftState(Context.runningState);        super.context.getLiftState().run();    }    //電梯停止是怎么發生的    @Override    public void stop() {        System.out.println("電梯停止了...");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

把邏輯放到沒一個狀態內,然后用context整合,做到添加狀態的時候也不需要修改原來的代碼。

狀態模式的定義:當一個對象內在狀態改變時允許其改變行為,這個對象 看起來像改變了其類。

狀態模式的核心是封裝,狀態的變更引起了行為的變更,從外部看起來就好像這個對象對應的類發生了改變一樣。

狀態模式的角色主要有三個:抽象狀態角色,具體狀態 角色,環境角色

狀態模式的優點:

  • 結構清晰,避免了過多的switch…case或者if…else語句的使用
  • 遵循設計原則,很多地體現了開閉原則和單一職責原則,每個狀態都是一個子類。
  • 封裝性比較好

    狀態模式的缺點:

  • 類膨脹

狀態模式的使用場景

  • 行為隨狀態改變而改變
  • 條件、分支判斷語句的替代者

    注意事項

  • 子類最好不要超過5個


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com