Skip to content

Commit d24ffdc

Browse files
committed
状态机模式
1 parent 3e19bd9 commit d24ffdc

File tree

2 files changed

+333
-0
lines changed

2 files changed

+333
-0
lines changed

fifteen-chapter/demo.html

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<html>
2+
<title>demo</title>
3+
<body>
4+
<script>
5+
var OffLightState = function(light){
6+
this.light = light;
7+
}
8+
9+
OffLightState.prototype.buttonWasPressed = function(){
10+
console.log('弱光');
11+
this.light.setState(this.light.weakLightState);
12+
}
13+
14+
var WeakLightState = function(light){
15+
this.light = light;
16+
}
17+
18+
WeakLightState.prototype.buttonWasPressed = function(){
19+
console.log('强光');
20+
this.light.setState(this.light.strongLightState);
21+
}
22+
23+
var StrongLightState = function(light){
24+
this.light = light;
25+
}
26+
27+
StrongLightState.prototype.buttonWasPressed = function(){
28+
console.log('关灯')
29+
this.light.setState(this.light.offLightState);
30+
};
31+
32+
var Light = function(){
33+
this.offLightState = new OffLightState(this);
34+
this.weakLightState = new WeakLightState(this);
35+
this.strongLightState = new StrongLightState(this);
36+
this.button = null;
37+
}
38+
39+
Light.prototype.init = function(){
40+
var button = document.createElement('button');
41+
var self = this;
42+
this.button = document.body.appendChild(button);
43+
this.button.innerHTML = '开关';
44+
this.curState = this.offLightState;
45+
46+
this.button.onclick = function(){
47+
self.curState.buttonWasPressed();
48+
}
49+
}
50+
51+
Light.prototype.setState = function(newState){
52+
this.curState = newState;
53+
};
54+
55+
var light = new Light();
56+
light.init();
57+
</script>
58+
</body>
59+
</html>

fifteen-chapter/index.js

+274
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
//状态模式
2+
// 状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变
3+
4+
//电灯程序
5+
var Light = function () {
6+
this.state = 'off';
7+
this.button = null;
8+
}
9+
10+
Light.prototype.init = function(){
11+
var button = document.createElement('button');
12+
var self = this;
13+
14+
button.innerHTML = '开关';
15+
this.button = document.body.appendChild(button);
16+
this.button.onclick = function(){
17+
self.buttonWasPressed();
18+
}
19+
}
20+
21+
Light.prototype.buttonWasPressed = function(){
22+
if(this.state === 'off'){
23+
console.log('开灯');
24+
this.state = 'on';
25+
}else if(this.state === 'on'){
26+
console.log('关灯');
27+
this.state = 'off';
28+
}
29+
};
30+
31+
var light = new Light();
32+
light.init();
33+
34+
//有的电灯不止2种状态
35+
Light.prototype.buttonWasPressed = function(){
36+
if(this.state === 'off'){
37+
conosle.log('弱光')
38+
this.state = 'weakLight';
39+
}else if(this.state === 'weakLight'){
40+
console.log('强光')
41+
this.state = 'strongLight';
42+
}else if(this.state === 'strongLight'){
43+
console.log('关灯')
44+
this.state = 'off';
45+
}
46+
};
47+
48+
//上面程序的缺点
49+
/*
50+
*违反封闭-开放原则
51+
*状态有关的行为,都被封装在buttonWasPressed方法里
52+
*状态切换非常不明显,仅仅表现对state变量赋值
53+
*状态之间的切换关系,堆砌if-else语句
54+
*/
55+
56+
// 状态模式改进电灯程序
57+
var offLightState = function(light){
58+
this.light = light;
59+
}
60+
61+
offLightState.prototype.buttonWasPressed = function(){
62+
console.log('弱光')
63+
this.light.setState(this.light.weakLightState);
64+
}
65+
66+
var weakLightState = function(light){
67+
this.light = light;
68+
}
69+
70+
weakLightState.prototype.buttonWasPressed = function(){
71+
console.log('强光');
72+
this.light.setState(this.light.strongLightState);
73+
}
74+
75+
var strongLightState = function(light){
76+
this.light = light;
77+
}
78+
79+
strongLightState.prototype.buttonWasPressed = function(){
80+
console.log('关灯')
81+
this.light.setState(this.light.offLightState);
82+
};
83+
84+
var Light = function(){
85+
this.offLightState = new offLightState(this);
86+
this.weakLightState = new weakLightState(this);
87+
this.strongLightState = new strongLightState(this);
88+
this.button = null;
89+
}
90+
91+
Light.prototype.init = function(){
92+
var button = document.createElement('button');
93+
var self = this;
94+
95+
this.button.innerHTML = '开关';
96+
this.button = document.body.append(button);
97+
this.curState = this.offLightState;
98+
99+
this.button.onclick = function(){
100+
self.curState.buttonWasPressed();
101+
}
102+
}
103+
104+
Light.prototype.setState = function(newState){
105+
this.curState = newState;
106+
};
107+
108+
var light = new Light();
109+
light.init();
110+
111+
//应用举例 文件上传的状态多
112+
// 文件上传的状态切换相比要复杂得多,控制文件上传的流程需要两个节点按钮,
113+
// 第一个用 于暂停和继续上传,第二个用于删除文件
114+
// 文件在扫描状态中,是不能进行任何操作的,既不能暂停也不能删除文件
115+
// 传过程中可以点击暂停按钮来暂停上传,暂停后点击同一个按钮会继续上传
116+
// 扫描和上传过程中,点击删除按钮无效,只有在暂停、上传完成、上传失败之后,才能删除文件
117+
118+
window.external.upload = function(state){
119+
console.log(state);
120+
}
121+
122+
var plugin = (function(){
123+
var plugin = document.createElement('embed');
124+
plugin.style.display = 'none';
125+
126+
plugin.type = 'application/txftn-webkit';
127+
128+
plugin.sign = function(){
129+
console.log('开始文件扫描')
130+
}
131+
132+
plugin.pause = function(){
133+
console.log('暂停文件上传')
134+
}
135+
136+
plugin.uploading = function(){
137+
console.log('开始文件上传');
138+
}
139+
140+
plugin.del = function(){
141+
console.log('删除文件上传');
142+
}
143+
144+
plugin.done = function(){
145+
console.log('文件上传完成')
146+
}
147+
148+
document.body.appendChild(plugin);
149+
150+
return plugin;
151+
152+
})()
153+
154+
var Upload = function(filename){
155+
this.plugin = plugin;
156+
this.filename = filename;
157+
this.button1 = null;
158+
this.button2 = null;
159+
this.state = 'sign';
160+
}
161+
162+
Upload.prototype.init = function(){
163+
var self = this;
164+
this.dom = document.createElement('div');
165+
this.dom.innerHTML =
166+
'<span>文件名称:'+ this.fileName +'</span>\
167+
<button data-action="button1">扫描中</button>\
168+
<button data-action="button2">删除</button>';
169+
170+
document.body.appendChild(this.dom);
171+
this.button1 = this.dom.querySelector('[data-action="button1"]')
172+
this.button2 = this.dom.querySelector('[data-action="button2"]')
173+
this.bindEvent();
174+
175+
};
176+
177+
Upload.prototype.bindEvent = function(){
178+
var self = this;
179+
this.button1.onclick = function(){
180+
if(self.state === 'sign'){
181+
console.log("扫描中,点击无效...")
182+
}else if (self.state === 'uploading'){
183+
self.changeState('pause');
184+
}else if(self.state === 'pause'){
185+
self.changeState('uploading');
186+
}else if(self.state === 'done'){
187+
console.log('文件已完成上传,点击无效')
188+
}else if(self.state === 'error'){
189+
console.log('文件上传失败,点击无效')
190+
}
191+
};
192+
193+
this.button2.onclick = function(){
194+
if(self.state === 'done' || self.state === 'error' || self.state === 'pause'){
195+
self.changeState('del')
196+
}else if (self.state === 'sign'){
197+
console.log('文件正在扫描中,不能删除')
198+
}else if(self.state === 'uploading'){
199+
console.log('文件正在上传中,不能删除')
200+
}
201+
}
202+
}
203+
204+
Upload.prototype.changeState = function(state){
205+
switch (state) {
206+
case 'sign':
207+
this.plugin.sign();
208+
this.button1.innerHTML = '扫描中,任何操作无效'
209+
break;
210+
case 'uploading':
211+
this.plugin.uploading();
212+
this.button1.innerHTML = '正在上传,点击暂停'
213+
break;
214+
case 'pause':
215+
this.plugin.pause();
216+
this.button1.innerHTML = '已暂停,点击继续上传'
217+
break;
218+
case 'done':
219+
this.plugin.done();
220+
this.button1.innerHTML = '上传完成'
221+
break;
222+
case 'error':
223+
this.button1.innerHTML = '上传失败'
224+
break;
225+
case 'del':
226+
this.plugin.del();
227+
this.dom.parentNode.removeChild(this.dom);
228+
break;
229+
}
230+
this.state = state;
231+
}
232+
233+
// 状态模式定义了状态与行为之间的关系,并将它们封装在一个类里。通过增加新的状态 类,很容易增加新的状态和转换
234+
//避免 Context 无限膨胀,状态切换的逻辑被分布在状态类中,也去掉了 Context 中原本过 5 多的条件分支
235+
// 用对象代替字符串来记录当前状态,使得状态的切换更加一目了然
236+
// Context 中的请求动作和状态类中封装的行为可以非常容易地独立变化而互不影响
237+
// 状态模式的缺点是会在系统中定义许多状态类,编写 20 个状态类是一项枯燥乏味的工作, 而且系统中会因此而增加不少对象。
238+
// 另外,由于逻辑分散在状态类中,虽然避开了不受欢迎的条 件分支语句,但也造成了逻辑分散的问题,我们无法在一个地方就看出整个状态机的逻辑
239+
240+
// 策略模式和状态模式的相同点是,它们都有一个上下文、一些策略或者状态类,上下文把请 求委托给这些类来执行
241+
242+
// JavaScript 可以非常方便地使用委托技术,并不需要事先让 一个对象持有另一个对象。下面的状态机选择了通过 Function.prototype.call 方法直接把请求委 托给某个字面量对象来执行
243+
// https:// github.com/jakesgordon/ javascript-state-machine [表驱动的有限状态机]
244+
245+
var Light = function(){
246+
this.currState = FSM.off; // 设置当前状态 this.button = null;
247+
};
248+
Light.prototype.init = function(){
249+
var button = document.createElement( 'button' ),
250+
self = this;
251+
button.innerHTML = '已关灯';
252+
this.button = document.body.appendChild( button );
253+
this.button.onclick = function(){
254+
self.currState.buttonWasPressed.call( self );
255+
};
256+
};
257+
var FSM = {
258+
off: {
259+
buttonWasPressed: function(){
260+
console.log( '关灯' );
261+
this.button.innerHTML = '下一次按我是开灯';
262+
this.currState = FSM.on;
263+
}
264+
},
265+
on: {
266+
buttonWasPressed: function(){
267+
console.log( '开灯' );
268+
this.button.innerHTML = '下一次按我是关灯';
269+
this.currState = FSM.off;
270+
}
271+
}
272+
}
273+
var light = new Light();
274+
light.init();

0 commit comments

Comments
 (0)