这篇文章并不刻意介绍状态机的学术概念,只想谈谈状态机的实际运用。如果想看系统性的学术介绍的话,可以查看百度百科。
百度百科:状态机
应用场景
同学们在工作中,有没有遇到过类似这样的代码。
代码语言:javascript复制if (/* n个判断条件 */) {
// 200行代码
} else if (/* n个判断条件 */)) {
// 300行代码
} else if (/* n个判断条件 */) {
// 量之大,一屏装不下
} else if (/* n个判断条件 */) {
// 量之大,一屏装不下
}
...................
如果安排你去维护这样一段代码,你会怎么做?
是浪费大量时间、枉死无数脑细胞去梳理其中的逻辑关系和方法,顺便抱怨一句“祖传代码真可恶”;还是会采用更好的结构(设计模式)去重构这些充满恶意的代码呢?
状态机模式就是用于处理这样一种复杂的逻辑判断结构的设计模式。
结构分析
在实现状态机时:
- 一个条件分支可以认为是一种状态(state)。
- 判断条件成立时(event),则执行对应的方法体(action)。
- 如果环境变量改变,以致不同分支的判断条件成立,则执行不同分支的方法体,这称为状态转换(transition)。
以上也就是状态机的四大概念:state、event、action、transition。
用法示例
假如业务想要做一个下滑翻屏的 Swiper 页面,每一屏都有不同的内容和动画。为了提高性能,我们希望用户进入该屏时,再播放该屏的动画,此时就可以使用状态机来实现这一场景。
代码语言:javascript复制class MovieActionState1 {
constructor () {}
update () {
// Action: 第一屏动画
}
}
class MovieActionState2 {
constructor () {}
update () {
// Action: 第二屏动画
}
}
class MovieActionState3 {
constructor () {}
update () {
// Action: 第三屏动画
}
}
class Movie {
actionState1; // State:第一屏
actionState2; // State:第二屏
actionState3; // State:第三屏
nowState; // 现态
constructor () {
this.actionState1 = new MovieActionState1()
this.actionState2 = new MovieActionState2()
this.actionState3 = new MovieActionState3()
this.nowState = this.actionState1
}
run (context) {
let ctx = context || this // 保证this指向的地址可被访问
if (ctx.nowState) {
ctx.nowState.update()
}
window.requestAnimationFrame(function () {
ctx.run(ctx)
})
}
toggleState (index) { // Event
switch (index) {
case 0:
this.nowState = this.actionState1 // Transition
break;
case 1:
this.nowState = this.actionState2
break;
case 2:
this.nowState = this.actionState3
break;
}
}
}
测试:
代码语言:javascript复制let movie = new Movie()
new Swiper ('.swiper-container', { // 需要引入第三方 Swiper 库
on: {
init () {
movie.run()
},
slideChange () {
movie.toggleState(this.activeIndex)
}
}
})
假如此时业务需要再加一屏动画,那么可以创建并实例化一个 MovieActionState4,并在 toggleState 中写上状态的触发条件和转换语句。这种结构清晰易读,要比写个几百上千行的条件分支结构要好维护多了。