状态模式的定义
状态模式:当一个对象的内部状态发生改变时,会导致其行为的改变,这看起来像是改变了对象。
类型:对象行为型模式
用途:解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
使用场景:
- 一个由一个或多个动态变化的属性导致发生不同行为的对象,在与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化,那么这个对象,就是有状态的对象
- 代码中包含大量与对象状态有关的条件语句,像是if else或switch case语句,且这些条件执行与否依赖于该对象的状态。
优点:
- 一个状态状态对应行为,封装在一个类里,更直观清晰,增改方便
- 状态与状态间,行为与行为间彼此独立互不干扰
- 避免事物对象本身不断膨胀,条件判断语句过多
- 每次执行动作不用走很多不必要的判断语句,用哪个拿哪个
缺点:
- 需要将事物的不同状态以及对应的行为拆分出来,有时候会无法避免的拆分的很细,有的时候涉及业务逻辑,一个动作拆分出对应的两个状态,动作就拆不明白了,过度设计
- 必然会增加事物类和动作类的个数,有时候动作类再根据单一原则,按照功能拆成几个类,会反而使得代码混乱,可读性降低
状态模式的一个模型——有限状态机 Finite-state machine
日常开发中很多具有多种状态的对象,都可以用有限状态机模型来模拟,一般都具有以下特点:
- 事物拥有多种状态,任一时间只会处于一种状态不会处于多种状态;
- 动作可以改变事物状态,一个动作可以通过条件判断,改变事物到不同的状态,但是不能同时指向多个状态,一个时间,就一个状态
- 状态总数是有限的;
github上有一个有限状态机的函数库javascript-state-machine,可以了解一下
https://github.com/jakesgordon/javascript-state-machine
演示范例1 —— 状态模式实现对话框的显示和隐藏
var Dialog = function(){ var _state = null; this.setState = function(state){ _state = state; } this.getState = function(){ return _state; } } var ShowState = function(){ this.doAction = function(dialog){ console.log("对Dialog设置显示状态:"); dialog.setState(this); } this.toString = function(){ console.log("显示中......"); } } var HideState = function(){ this.doAction = function(dialog){ console.log("对Dialog设置隐藏状态:"); dialog.setState(this); } this.toString = function(){ console.log("已隐藏......"); } } var dialog = new Dialog(); var showState = new ShowState(); var hideState = new HideState(); showState.doAction(dialog); //对Dialog设置显示状态: dialog.getState().toString(); //显示中...... hideState.doAction(dialog); //对Dialog设置隐藏状态 dialog.getState().toString(); //已隐藏......
例子里Dialog对象有两种状态,显示和隐藏,我把两种状态提取出来,使得状态的管理更加灵活。在这个例子里面Dialog称之为环境类,环境类又称为上下文类,他拥有多种状态。环境类内部需要维护一个state对象用来定义当前状态。HideState,ShowState称之为状态类,对应环境类的一个具体状态,toString称之为状态类的行为,每一个状态类的行为都有所不同。
演示范例2 —— 状态模式实现角色扮演游戏(如魂斗罗)
class Contra { constructor () { //存储当前待执行的动作 们 this._currentstate = {}; } //添加动作 changeState (){ //清空当前的动作集合 this._currentstate = {}; //遍历添加动作 Object.keys(arguments).forEach( (i) => this._currentstate[arguments[i]] = true ) return this; } //执行动作 contraGo (){ //当前动作集合中的动作依次执行 Object.keys(this._currentstate).forEach( (k) => Actions[k] && Actions[k].apply(this) ) return this; } }; const Actions = { up : function(){ //向上跳 console.log('up'); }, down : function(){ //趴下 console.log('down'); }, forward : function(){ //向前跑 console.log('forward'); }, backward : function(){ //往老家跑 console.log('backward'); }, shoot : function(){ //开枪吧 console.log('shoot'); }, }; var littlered = new Contra(); littlered.changeState('shoot','up').contraGo();
控制台会输出: shoot up
状态模式,将条件判断的结果转化为状态对象内部的状态(代码中的up,down,backward,forward),内部状态通常作为状态对象内部的私有变量(this._currentState),然后提供一个能够调用状态对象内部状态的接口方法对象(changeState,contraGo),这样对状态的改变,对状态方法的调用的修改和增加也会很容易,方便了对状态对象中内部状态的管理。
同时,状态模式将每一个条件分支放入一个独立的类中,也就是代码中的Actions。这使得你可以根据对象自身的情况将对象的状态(动作——up,down,backward,forward)作为一个对象(Actions.up,Actions.down这样),这一对象可以不依赖于其他对象而独立变化(一个行为一个动作,互不干扰)。
可以看出,状态模式就是一种适合多种状态场景下的设计模式,改写之后代码更加清晰,提高代码的维护性和扩展性,不用再牵一发动全身
演示范例3 —— 状态模式实现红绿灯
var trafficLight = (function () { var currentLight = null; return { change: function (light) { currentLight = light; currentLight.go(); } } })(); function RedLight() { } RedLight.prototype.go = function () { console.log("this is red light"); } function GreenLight() { } GreenLight.prototype.go = function () { console.log("this is green light"); } function YellowLight() { } YellowLight.prototype.go = function () { console.log("this is yellow light"); } trafficLight.change(new RedLight()); trafficLight.change(new YellowLight());
trafficLight是一个红绿灯的实例,传入一个构造函数,对象暴露change方法改变内部状态,也就是灯的颜色,change接收的同样是一个状态的对象,调用对象的方法触发响应的动作,这里的动作都叫go,不同颜色的灯对象有着不同的go的实现。
通过传入灯对象到change方法中,从而改变红绿灯的状态,触发其相应的处理程序,这就是一个典型的状态模式的应用。
更多设计模式详见——js设计模式【详解】总目录
https://blog.csdn.net/weixin_41192489/article/details/116154815