js常用设计模式11-状态模式
js常用设计模式1-单例模式
js常用设计模式2-策略模式
js常用设计模式3-代理模式
js常用设计模式4-发布-订阅模式
js常用设计模式5-命令模式
js常用设计模式6-组合模式
js常用设计模式7-享元模式
js常用设计模式8-职责链模式
js常用设计模式9-中介者模式
js常用设计模式10-装饰者模式
js常用设计模式11-状态模式
状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变
1,一个简单的电灯程序
我们设想这么一个场景:有一个电灯,灯开的时候,按开关,啪一下就关了;灯关的时候,按开关,啪一下就开了。
同一个开关,不同的状态下执行,表现出来的行为和结果不同。
<script>
/**
* 一个简单的开关灯状态
*/
var Light = function () {
this.state = 'off'
this.button = null
}
Light.prototype.init = function () {
var button = document.createElement('button')
self = this
button.innerHTML = '开关'
this.button = document.body.appendChild(button)
this.button.onclick = function () {
self.buttonWasPressed()
}
}
Light.prototype.buttonWasPressed = function () {
if (this.state === 'off') {
console.log('开灯')
this.state = 'on'
} else {
console.log('关灯')
this.state = 'off'
}
}
var light = new Light()
light.init()
</script>
现在我们想增加一点功能,把开灯改成弱光、强光,状态的顺序就变成了:关灯->弱光->强光。
现在我们来修改代码:
// 增加功能:如果需要改变光亮,关灯->弱光->强光,该怎么办呢?
Light.prototype.buttonWasPressed = function () {
if (this.state === 'off') {
console.log('弱光')
this.state = 'weakLight'
} else if (this.state === 'weakLight') {
console.log('强光')
this.state = 'strongLight'
} else if (this.state === 'strongLight') {
console.log('关灯')
this.state = 'off'
}
}
现在我们看到了,这段代码的写法是有问题的。
缺点:
1,buttonWasPressed违反 开放-封闭 原则,每次新增或修改light的状态都要改动代码,且代码难以维护
2,代码逻辑封装在buttonWasPressed中,light状态增加,buttonWasPressed的代码也会急剧膨胀
3,状态切换不明显,只在state上体现出来,很容易漏掉
2,改进的灯泡程序
一般的封装是封装对象的行为,而不是对象的状态。状态模式不一样,它封装的就是事物的状态,每个状态都有自己的类,跟这个状态有关的行为都被封装在类的内部。
对于我们现在的功能来说,button被按下的时候,只需要在上下文中,把这个请求委托给当前的状态对象即可。
同时我们可以状态的切换规则写在状态类中,这样就不用写if else语句了:
// 添加三个状态类
var OffLightState = function (light) {
this.light = light
}
OffLightState.prototype.buttonWasPressed = function () {
console.log('弱光')
this.light.setState(this.light.weakLightState)
}
var WeakLightState = function (light) {
this.light = light
}
WeakLightState.prototype.buttonWasPressed = function () {
console.log('强光')
this.light.setState(this.light.strongLightState)
}
var StrongLightState = function (light) {
this.light = light
}
StrongLightState.prototype.buttonWasPressed = function () {
console.log('关灯')
this.light.setState(this.light.offLightState)
}
改写Light类,在Light类中给每个状态类都创建对象,这样的话我们的状态就很清晰:
var Light = function () {
this.offLightState = new OffLightState(this)
this.weakLightState = new WeakLightState(this)
this.strongLightState = new StrongLightState(this)
this.button = null
}
button按下的时候,不需要light对象自己处理了,交给状态对象:
Light.prototype.init = function () {
var button = document.createElement('button')
self = this
this.button = document.body.appendChild(button)
this.button.innerHTML = '开关'
//设置当前状态
this.curState = this.offLightState
this.button.onclick = function () {
self.curState.buttonWasPressed()
}
}
提供一个修改light对象状态的方法:
Light.prototype.setState = function (newState) {
this.curState = newState
}
现在我们来try一try:
var light = new Light()
light.init()
3,light增加新的状态
我们现在需要增加一种“超强光”的状态,要怎么做呢?
(1)新建状态类:
//现在我们添加一个新的状态:超强光
var SuperStrongLightState = function (light) {
this.light = light
}
SuperStrongLightState.prototype.buttonWasPressed = function () {
console.log('关灯')
this.light.setState(this.light.offLightState)
}
(2)修改Light类:
//在Light类中添加新的状态
var Light = function () {
this.offLightState = new OffLightState(this)
this.weakLightState = new WeakLightState(this)
this.strongLightState = new StrongLightState(this)
this.superStrongLightState = new SuperStrongLightState(this)
this.button = null
}
(3)修改强光类的逻辑:
//修改强光类的逻辑
StrongLightState.prototype.buttonWasPressed = function () {
console.log('超强光')
this.light.setState(this.light.superStrongLightState)
}
4,关键之处
关键之处在于在Light中新建状态类对象时,都把this也就是light对象传进去了,这样所有的状态类中都可以修改同一个light对象了,这实际上起到了委托的效果。