Javascript-设计模式_状态模式
前言
状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变,即对象行为是基于状态来改变的,内部的状态转化,导致了行为表现形式不同
当电灯开着,此时按下开关,电灯会切换到关闭状态;再按一次开关,电灯又将被打开。同一个开关在不同的状态下,表现出来的行为是不一样的
场景条件--有限状态机
-
状态总数(state)是有限的
-
任一时刻,只处在一种状态之中
-
某种条件下,会从一种状态转变(transition)到另一种状态
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类
解释:
-
将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态发生改变时,会带来不同的行为变化
-
使用的对象,在不同的状态下具有截然不同的行为(委托效果)
谈到封装,一般优先考虑封装对象的行为,而不是对象的状态,但在状态模式中刚好相反,状态模式的关键是把事物的每种状态都封装成单独的类
示例
点灯程序 (弱光 –> 强光 –> 关灯)循环
/* 关灯 */ const OffLightState = light => { this.light = light } /* 弱光 */ const WeakLightState = light => { this.light = light } /* 强光 */ const StrongLightState = light => { this.light = light } const Light = () => { // 开关状态 this.offLight = new OffLightState(this) this.weakLight = new WeakLightState(this) this.strongLight = new StrongLightState(this) // 快关按钮 this.button = null } Light.prototype.init = () => { const button = document.createElement('button') const self = this this.button = document.body.appendChild(button) this.button.innerHTML = '开关' this.currentState = this.offLight this.button.click = () => { self.currentState.buttonWasPressed() } } /* 让抽象父类的抽象方法直接抛出一个异常(避免状态子类未实现buttonWasPressed方法) */ Light.prototype.buttonWasPressed = () => { throw new Error('父类的buttonWasPressed方法必须被重写') } Light.prototype.setState = newState => { this.currentState = newState } /* 关灯 */ OffLightState.prototype = new Light() // 继承抽象类 OffLightState.prototype.buttonWasPressed = () => { console.log('关灯!') this.light.setState(this.light.weakLight) } /* 弱光 */ WeakLightState.prototype = new Light() WeakLightState.prototype.buttonWasPressed = () => { console.log('弱光!') this.light.setState(this.light.strongLight) } /* 强光 */ StrongLightState.prototype = new Light() StrongLightState.prototype.buttonWasPressed = () => { console.log('强光!') this.light.setState(this.light.offLight) }
性能优化点
-
如何管理状态对象的创建和销毁
第一种仅当state对象被需要时才创建并随后销毁(state对象比较庞大,优先选择)
另一种是一开始就创建好所有的状态对象,并且始终不销毁它们(状态改变频繁)
-
利用享元模式共享一个state对象
优缺点
优点
封装了转化规则,对于大量分支语句,可以考虑使用状态类进一步封装。 每个状态都是确定的,所以对象行为是可控的
缺点
状态模式的关键是将事物的状态都封装成单独的类,这个类的各种方法就是“此种状态对应的表现行为”, 因此状态类会增加程序开销
总结
状态模式的使用场景也特别明确,有如下两点
-
一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为
-
一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态。状态通常为一个或多个枚举常量的表示
简而言之,当遇到很多同级 if-else或者 switch的时候,可以使用状态模式来进行简化