Skip to content

Commit 4f78367

Browse files
committed
186
1 parent 299a60a commit 4f78367

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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

Comments
 (0)