天天看点

js设计模式【详解】—— 状态模式

目录

​​状态模式的定义​​

​​状态模式的一个模型——有限状态机 Finite-state machine​​

​​演示范例1 —— 状态模式实现对话框的显示和隐藏​​

​​演示范例2 —— 状态模式实现角色扮演游戏(如魂斗罗)​​

​​演示范例3 —— 状态模式实现红绿灯​​

状态模式的定义

状态模式:当一个对象的内部状态发生改变时,会导致其行为的改变,这看起来像是改变了对象。

类型:对象行为型模式

用途:解决系统中复杂对象的状态转换以及不同状态下行为的封装问题

使用场景:

  1. 一个由一个或多个动态变化的属性导致发生不同行为的对象,在与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化,那么这个对象,就是有状态的对象
  2. 代码中包含大量与对象状态有关的条件语句,像是if else或switch case语句,且这些条件执行与否依赖于该对象的状态。

优点:

  1. 一个状态状态对应行为,封装在一个类里,更直观清晰,增改方便
  2. 状态与状态间,行为与行为间彼此独立互不干扰
  3. 避免事物对象本身不断膨胀,条件判断语句过多
  4. 每次执行动作不用走很多不必要的判断语句,用哪个拿哪个

缺点:

  1. 需要将事物的不同状态以及对应的行为拆分出来,有时候会无法避免的拆分的很细,有的时候涉及业务逻辑,一个动作拆分出对应的两个状态,动作就拆不明白了,过度设计
  2. 必然会增加事物类和动作类的个数,有时候动作类再根据单一原则,按照功能拆成几个类,会反而使得代码混乱,可读性降低

状态模式的一个模型——有限状态机 Finite-state machine

 日常开发中很多具有多种状态的对象,都可以用有限状态机模型来模拟,一般都具有以下特点:

  1. 事物拥有多种状态,任一时间只会处于一种状态不会处于多种状态;
  2. 动作可以改变事物状态,一个动作可以通过条件判断,改变事物到不同的状态,但是不能同时指向多个状态,一个时间,就一个状态
  3. 状态总数是有限的;

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的实现。