状态模式
策略模式和状态模式是双胞胎,在出生时才分开。你已经知道,策略模式是围绕可以互换的算法来创建成功业务的,然而,状态走的是更崇高的路,它通过改变对象内部的状态来帮助对象控制自己的行为。
定义状态模式
先看看定义:状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类
例题
自动糖果售卖机,糖果机的控制器需要的工作流程如下图
从上面的状态图中可以找到所有的状态:
我们可以创建一个实例变量来持有目前的状态,然后定义每个状态的值:
1 2 3 4 5 6 7 |
|
现在,我们将所有系统中可以发生的动作整合起来:
“投入25分钱”,“退回25分钱”,“转动曲柄”,“发放糖果”
这些动作是糖果机的接口,这是你能对糖果机做的事情,
调用任何一个动作都会造成状态的转换,
发放糖果更多是糖果机的内部动作,机器自己调用自己。
我们创建一个类,它的作用就像是一个状态机,每一个动作,我们都创建了一个对应的方法,这些方法利用条件语句来决定在每个状态内什么行为是恰当的。比如对“投入25分钱”这个动作来说,我们可以把对应方法写成下面的样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
初步代码
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 |
|
尽管程序完美运行,但还是躲不掉需求变更的命运
现在糖果公司要求:当曲柄被转动时,有10%的几率掉下来的是两个糖果。(氪金扭蛋)
再回看一下我们的初步代码,想要实现新的需求将会变得非常麻烦:
- 必须新增一个中奖的“赢家”状态。
- 必须在每一个方法添加新的判断条件来处理“赢家”状态。
- 转动把手的方法中还需要检查目前状态是否是“赢家”再决定切换到“赢家”状态行为还是正常出售行为。
在现有代码基础上做增加将会很麻烦,也不利与以后的维护,扩展性差。
回顾一下第一章的策略模式中的设计原则:
找出应用中可能需要变化之处,把他们独立出来
将状态独立出来,封装成一个类,都实现State接口,类图如下:
新的设计想法如下:
- 首先,我们定义一个
State
接口,在这个接口内,糖果机的每个动作都有一个对应的方法 - 然后为机器的每个状态实现状态类,这些类将负责在对应的状态下进行机器的行为
- 最后,我们要摆脱旧的条件代码,取而代之的方式是,将动作委托到状态类
代码
定义一个State接口
1 2 3 4 5 6 |
|
为机器的每个状态实现状态类:
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 |
|
糖果机类:
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 |
|
以上就是用状态模式实现的,仔细观察你会发现状态模式其实和策略模式很像,来看看状态模式的类图:
状态模式的类图其实和策略模式完全一样!
状态模式与策略模式
这两个模式的差别在于它们的“意图”
- 以状态模式而言,我们将一群行为封装在状态对象中,context的行为随时可委托到那些状态对象中的一个,随着时间而流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此,context的行为也会跟着改变,但是context的客户对于状态对象了解不多,甚至根本是浑然不觉。
- 以策略模式而言,客户通常主动指定Context所要组合的策略对象时哪一个。现在,固然策略模式让我们具有弹性,能够在运行时改变策略,但对于某个context对象来说,通常都只有一个最适当的策略对象。
- 一般的,我们把策略模式想成是除了继承之外的一种弹性替代方案,如果你使用继承定义了一个类的行为,你将被这个行为困住,是指要修改它都很难,有了策略模式,你可以通过组合不同的对象来改变行为。
- 我们把状态模式想成是不用在context中放置许多条件判断的替代方案,通过将行为包装进状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。
模式区分
状态模式:封装基于状态的行为,并将行为委托到当前状态
策略模式:将可以互换的行为封装起来。然后使用委托的方法,觉得使用哪一个行为
模板方法模式:由子类决定如何实现算法中的某些步骤
要点
(1)状态模式允许一个对象基于内部状态而拥有不同的行为。
(2)和程序状态机(PSM)不同,状态模式用类来表示状态。
(3)Context会将行为委托给当前状态对象。
(4)通过将每一个状态封装进一个类,我们把以后需要做的任何改变局部化了。
(5)状态模式和策略模式有相同的类图,但是他们的意图不同。
(6)策略模式通常会用行为或算法配置Context类。
(7)状态模式允许Context随着状态的改变而改变行为。
(8)状态转换可以有State类或Context类控制。
(9)使用状态模式通常会导致设计中类的数目大量增加。
(10)状态栏可以被多个Context实例共享。