|
| 1 | +# State(状态模式) |
| 2 | + |
| 3 | +State(状态模式)属于行为型模式。 |
| 4 | + |
| 5 | +**意图:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。** |
| 6 | + |
| 7 | +简单来说,就是将 “一个大 class + 一堆 if else” 替换为 “一堆小 class”。一堆小 class 就是一堆状态,用一堆状态代替 if else 会更好拓展与维护。 |
| 8 | + |
| 9 | +## 举例子 |
| 10 | + |
| 11 | +如果看不懂上面的意图介绍,没有关系,设计模式需要在日常工作里用起来,结合例子可以加深你的理解,下面我准备了三个例子,让你体会什么场景下会用到这种设计模式。 |
| 12 | + |
| 13 | +### 团队接口人 |
| 14 | + |
| 15 | +团队是由很多同学组成的,但有一位接口人 TL,这位 TL 可能一会儿和产品经理谈需求,一会儿和其他 TL 谈规划,一会儿和 HR 谈人事,总之要做很多事情,很显然一个人是忙不过来的。TL 通过将任务分发给团队中每个同学,而不让他们直接和产品经理、其他 TL、HR 接触,那么这位 TL 的办事效率就会相当高,因为每个同学只负责一块具体的业务,而 TL 在不同时刻叫上不同的同学,让他们出面解决他们负责的专业领域问题,那么在外面看,这位 TL 团队能力很广,在内看,每个人负责的事情也比较单一。 |
| 16 | + |
| 17 | +### 台灯按钮 |
| 18 | + |
| 19 | +我们经常会看到只有一个按钮的台灯,但是可以通过按钮调节亮度,大概是如下一个循环 “关 -> 弱光 -> 亮 -> 强光 -> 关”,那么每次按按钮后,要跳转到什么状态,其实和当前状态有关。我们可以用 if else 解决这个问题,也可以用状态模式解决。 |
| 20 | + |
| 21 | +用状态模式解决,就是将这四个状态封装为四个类,每个类都执行按下按钮后要跳转到的状态,这样未来新增一种模式,只要改变部分类即可。 |
| 22 | + |
| 23 | +### 数据库连接器 |
| 24 | + |
| 25 | +在数据库连接前后,这个连接器的状态显然非常不同,我们如果仅用一个类描述数据库连接器,则内部免不了写大量分支语句进行状态判断。那么此时有更好的方案吗?状态模式告诉我们,可以创建多个不同状态类,比如连接前、连接中、连接后三种状态类,在不同时刻内部会替换为不同的子类,它们都继承同样的父类,所以外面看上去不需要感知内部的状态变化,内部又可以进行状态拆分,进行更好的维护。 |
| 26 | + |
| 27 | +## 意图解释 |
| 28 | + |
| 29 | +**意图:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。** |
| 30 | + |
| 31 | +重点在 “内部状态” 的理解,也就是状态改变是由对象内部触发的,而不是外部,所以 **外部根本无需关心对象是否用了状态模式**,拿数据库连接器的例子来说,不管这个类是用 if else 堆砌的,还是用状态模式做的,都完全不妨碍它对外提供的稳定 API(接口问题),所以状态模式实质上是一种内聚的设计模式。 |
| 32 | + |
| 33 | +## 结构图 |
| 34 | + |
| 35 | +<img width=600 src="https://img.alicdn.com/imgextra/i1/O1CN01tbZ0bQ1w8xcUgbWTJ_!!6000000006264-2-tps-1350-486.png"> |
| 36 | + |
| 37 | +- State: 状态接口,类比为台灯状态。 |
| 38 | +- ConcreteState: 具体状态,都继承于 State,类比为台灯的强光、弱光状态。 |
| 39 | + |
| 40 | +## 代码例子 |
| 41 | + |
| 42 | +下面例子使用 typescript 编写。 |
| 43 | + |
| 44 | +```typescript |
| 45 | +// 定义状态接口 |
| 46 | +interface State { |
| 47 | + // 模拟台灯点亮 |
| 48 | + show: () => string |
| 49 | +} |
| 50 | + |
| 51 | +class Light1 implements State { |
| 52 | + constructor(context: Context) { |
| 53 | + this.context = context |
| 54 | + } |
| 55 | + |
| 56 | + show() { |
| 57 | + return '关灯' |
| 58 | + } |
| 59 | + |
| 60 | + // 按下按钮 |
| 61 | + public click() { |
| 62 | + this.context.setState(new Light2(this.context)) |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +class Light2 implements State { |
| 67 | + constructor(context: Context) { |
| 68 | + this.context = context |
| 69 | + } |
| 70 | + |
| 71 | + show() { |
| 72 | + return '弱光' |
| 73 | + } |
| 74 | + |
| 75 | + // 按下按钮 |
| 76 | + public click() { |
| 77 | + this.context.setState(new Light3(this.context)) |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +class Light3 implements State { |
| 82 | + constructor(context: Context) { |
| 83 | + this.context = context |
| 84 | + } |
| 85 | + |
| 86 | + show() { |
| 87 | + return '亮' |
| 88 | + } |
| 89 | + |
| 90 | + // 按下按钮 |
| 91 | + public click() { |
| 92 | + this.context.setState(new Light4(this.context)) |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +class Light4 implements State { |
| 97 | + constructor(context: Context) { |
| 98 | + this.context = context |
| 99 | + } |
| 100 | + |
| 101 | + show() { |
| 102 | + return '强光' |
| 103 | + } |
| 104 | + |
| 105 | + // 按下按钮 |
| 106 | + public click() { |
| 107 | + this.context.setState(new Light1(this.context)) |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | +// 台灯 |
| 112 | +public class Lamp { |
| 113 | + // 当前状态 |
| 114 | + private currentState = new Light1(this) |
| 115 | + |
| 116 | + protected setState(state: State) { |
| 117 | + this.currentState = state |
| 118 | + } |
| 119 | + |
| 120 | + // 按下按钮 |
| 121 | + public click() { |
| 122 | + this.getState().click() |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +const lamp = new Lamp() // 关闭 |
| 127 | +lamp.click() // 弱光 |
| 128 | +lamp.click() // 亮 |
| 129 | +lamp.click() // 强光 |
| 130 | +lamp.click() // 关闭 |
| 131 | +``` |
| 132 | + |
| 133 | +其实有很多种方式来实现,不必拘泥于形式,大体上只要保证由多个类实现不同状态,每个类实现到下一个状态切换就好了。 |
| 134 | + |
| 135 | +## 弊端 |
| 136 | + |
| 137 | +该用 if else 的时候还是要用,不要但凡遇到 if else 就使用状态模式,那样就是书读傻了。一定要判断,是否各状态间差异很大,且使用状态模式后维护性比 if else 更好,才应该用状态模式。 |
| 138 | + |
| 139 | +## 总结 |
| 140 | + |
| 141 | +在合适场景下,状态模式可以使代码更符合开闭原则,每个类独立维护时,逻辑也更精简、聚焦,更易维护。 |
| 142 | + |
| 143 | +> 讨论地址是:[精读《设计模式 - State 状态模式》· Issue #303 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/303) |
| 144 | +
|
| 145 | +**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。** |
| 146 | + |
| 147 | +> 关注 **前端精读微信公众号** |
| 148 | +
|
| 149 | +<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg"> |
| 150 | + |
| 151 | +> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)) |
0 commit comments