js设计原则-状态模式
状态模式-State
State 介绍
状态模式( State ):当一个对象状态发生改变时,会导致其行为的改变,这看起来像是改变了对象。
-
一个对象有状态变化
-
每次状态变化都会触发一个逻辑
-
不能总是用 if…else… 来控制
State 示例 & UML
- 红黄绿灯
State 代码
// 状态(红灯、黄灯、绿灯)
class State {
constructor(color) {
this.color = color;
}
handle(context) {
console.log(`turn to ${this.color} light`);
context.setState(this);
}
}
// 主体
class Context {
constructor() {
this.state = null;
}
// 获取状态
getState() {
return this.state;
}
setState(state) {
this.state = state;
}
}
let context = new Context();
let red = new State('red');
let yellow = new State('yellow');
let green = new State('green');
// 红灯亮了
red.handle(context);
console.log(context.getState());
// 黄灯亮了
yellow.handle(context);
console.log(context.getState());
// 绿灯亮了
green.handle(context);
console.log(context.getState());
状态的获取和主体改变状态是分离的。
State 场景
➊ 有限状态机
➋ 写一个简单的 Promise
➌ 超级玛丽
有限状态机-state
-
有限个状态、以及在这些状态之间的变化
-
如交通信号灯
-
使用开源 lib : javascript-state-matchine
https://github.com/jakesgordon/javascript-state-machine
npm i javascript-state-machine --save
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<p>有限状态机</p>
<button id="btn"></button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="./03-javascript-state-machine.js"></script>
<script>
// 状态机模型
var fsm = new StateMachine({
init: '收藏', // 初始状态,待收藏
transitions: [
{
name: 'doStore',
from: '收藏',
to: '取消收藏'
},
{
name: 'deleteStore',
from: '取消收藏',
to: '收藏'
}
],
methods: {
// 执行收藏
onDoStore: function () {
alert('收藏成功')
updateText()
},
// 取消收藏
onDeleteStore: function () {
alert('已取消收藏')
updateText()
}
}
})
var $btn = $('#btn')
// 点击事件
$btn.click(function () {
if (fsm.is('收藏')) {
fsm.doStore(1)
} else {
fsm.deleteStore()
}
})
// 更新文案
function updateText() {
$btn.text(fsm.state)
}
// 初始化文案
updateText()
</script>
</body>
</html>
fsm.doStore() 执行时, 改变 transitions 中 name 的 state 的状态从 from 到 to, 同时执行 methods 中命名的函数 onDoStore。
写一个 Promise
-
回顾 Promise 的语法
-
分析: Promise 就是一个有限状态机
-
Promise 三种状态:pending 、fullfilled、rejected
import StateMachine from 'javascript-state-machine';
// 定义 Promise
class MyPromise {
constructor(fn) {
this.successList = [];
this.failList = [];
fn(() => {
// resolve 函数
fsm.resolve(this);
}, () => {
// reject函数
fsm.reject(this);
})
}
then(successFn,failFn) {
this.successList.push(successFn);
this.failList.push(failFn);
}
}
// 模型
var fsm = new StateMachine({
init: 'pending',
transitions: [
{
name: 'resolve',
from: 'pending',
to: 'fullfilled'
},
{
name: 'reject',
from: 'pending',
to: 'rejected'
}
],
methods: {
// 成功
onResolve: function(state, data) {
// state - 当前状态机实例
// data - fsm.resolve(XXX) 传递的参数
data.successList.forEach(fn => fn())
},
// 失败
onReject: function(state, data) {
data.failList.forEach(fn => fn())
}
}
})
// test
function loadImg(src) {
const promise = new Promise((resolve,reject) => {
var img = document.createElement('img');
img.onload = function() {
resolve(img);
}
img.onerror = function() {
reject('图片加载失败')
}
img.src = src;
})
return promise;
}
var src = 'https://www.imooc.com/static/img/index/logo.png';
var result = loadImg(src);
result.then(function(img) {
console.log('ok1');
return img;
}, function() {
})
result.then(function(img) {
console.log('ok2');
})
超级玛丽-state
状态模式目的:
将条件判断的不同结果转化为状态对象的内部状态,既然是状态对象的内部状态,所以一般作为状态对象内部的私有变量,然后提供一个能够调用状态对象内部状态的接口方法对象。
状态模式思路:
首相创建一个状态对象,内部保存状态变量,然后内部封装好每种动作对应的状态,最后状态对象返回一个接口对象,它可以对内部状态修改或者调用。
// 创建超级玛丽状态类
var MarryState = function() {
// 内部状态私有变量
var _currentState = {},
// 动作与状态方法映射
states = {
jump: function() {
console.log('jump');
},
move: function() {
console.log('move');
},
shoot: function() {
console.log('shoot');
},
squat: function() {
console.log('squat');
}
};
// 动作控制类
var Action = {
// 改变状态方法
changeState: function() {
// 组合动作通过传递多个参数实现
var arg = arguments;
// 重置内部状态
_currentState = {};
// 如果有动作则添加动作
if (arg.length) {
// 遍历动作
for (var i = 0; i < arg.length; i++) {
// 向内部状态中添加动作
_currentState[arg[i]] = true;
}
}
// 返回动作控制类
return this;
},
// 执行动作
goes: function() {
console.log('触发一次动作');
// 遍历内部状态保存的动作
for (var i in _currentState) {
// 如果该动作存在则执行
states[i] && states[i]()
}
return this;
}
}
// 返回接口方法 change、goes
return {
change: Action.changeState,
goes: Action.goes
}
}
// 创建一个超级玛丽
var marry = new MarryState();
marry.change('jump', 'shoot')
.goes()
.goes()
.change('shoot')
.goes()
结果显示:
触发一次动作
jump
shoot
触发一次动作
jump
shoot
触发一次动作
shoot
State 设计原则验证
-
将状态对象和主题2对象分离,状态呢的变化逻辑单独处理
-
符合开放封闭原则
状态模式既是解决程序中臃肿的分支判断语句问题,将每个分支转化为一种状态独立出来,方便每种状态的管理又不至于每次执行时遍历所有分支。最终目的是简化分支判断流程。