设计模式之状态模式

《Head First 设计模式》阅读笔记

Posted by wangtiegang on March 15, 2020

状态模式允许对象在内部状态改变时改变它们的行为,对象看起来像修改了它的类。

简单来说就是有些对象的行为会因为状态不同而不同,比如…想不到合适的例子,拿书上的例子来说就是,假设有个自动售卖糖果机,存在投币,退币,出货几种操作,仔细分析,就可以知道这中间存在几种状态,并且之间具有转换关系。

state-1

用户可以通过投币、退币、出货几种操作改变状态,重点是这三种操作在任何状态下都是可以进行的,所以不同状态下同一种操作就会有不同的行为,如果用代码来实现,就变成了大量的 if 判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 出货
public void sell(){
    if (/*等待投币*/){
        // 提示未投币
    }

    if (/*已投币*/){
        // 出货
    }

    if (/*缺货*/){
        // 提示缺货,退币
    }

    // ...
}

首先这个代码肯定是可以解决问题的,但是一旦后期增加了其他功能,多了几种状态,就需要修改每一个操作里面的 if 语句,这样不利于维护和扩展,违反了开放关闭原则,而状态模式的出现,就是为了解决这些问题的。

状态模式是把每种状态封装成不同的类,具有所有可能的操作,这样操作的时候只要考虑这种状态下的行为就行了,这个就是封装“变化”,具体实现如下

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
 * 售货机,这部分是不变的部分
 */
public class VendingMachine {
    /**
     * 全部状态,此处只列举两种
     */
    State waitCoin;
    State waitSell;

    /**
     * 售货机当前状态
     */
    private State state;

    /**
     * 构造时初始化状态
     */
    public VendingMachine(){
        waitCoin = new WaitCoinState(this);
    }

    /**
     * 将操作委托给当前状态处理,随着状态的改变,操作的行为也会变化
     */
    public void coin(){
        this.state.coin();
    }

    public void returnCoin(){
        this.state.returnCoin();
    }

    public void sell(){
        this.state.sell();
    }

    /**
     * 暴露状态给外部操作
     * @return
     */
    public State getState() {
        return state;
    }

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

}


/**
 * 状态抽象类,也可以是个接口,如果某些操作在不同状态下都一样
 * 则抽象类更好,可以实现通用逻辑,直接让子类继承
 */
public abstract class State {

    /**
     * 持有售货机实例,就可以操作售货机了
     */
    protected VendingMachine vendingMachine;

    public void coin(){
        // 修改状态为已投币待出货
        vendingMachine.setState(vendingMachine.waitSell);
    }

    public void returnCoin(){
        // 退币
        // 修改状态为等待投币
        vendingMachine.setState(vendingMachine.waitCoin);

    }

    public void sell(){
        // 出货
    }

}


/**
 * 等待投币状态
 */
public class WaitCoinState extends State {

    public WaitCoinState(VendingMachine vendingMachine){
        // 构造方法传入售货机
        this.vendingMachine = vendingMachine;
    }

    @Override
    public void coin() {
        // 投币后
        super.coin();
    }

    @Override
    public void returnCoin() {
        // 提示没有投币,不能退币,状态不变
    }

    @Override
    public void sell() {
        // 提示没有投币,不能出货
    }


}

/**
 * 等待出货状态
 */
public class WaitSellState extends State {

    public WaitSellState(VendingMachine vendingMachine){
        // 构造方法传入售货机
        this.vendingMachine = vendingMachine;
    }

    @Override
    public void coin() {
        // 提示已投币
    }

    @Override
    public void returnCoin() {
        // 直接调用父类方法
        super.returnCoin();
    }

    @Override
    public void sell() {
        // 提示没有投币,不能出货
    }


}

状态模式跟策略模式很像,但是有所区别,策略模式像是选定了策略,就用这种策略把事情做完了。状态模式是随着状态的变化,事情交给不同的策略去做,同一件事会用到很多种策略。