状态模式

2016-10-12 曹强 更多博文 » 博客 » GitHub »

javascript

原文链接 https://ronghuaxueleng.github.io/2016/10/12/JavaScript%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5-18%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。



状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。

电灯程序

首先给一个不用状态模式的电灯程序例子:

var Light = function() {
  this.state = 'off'; //电灯初始状态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 if (this.state === 'on') {
    console.log('关灯');
    this.state = 'off';
  }
};

var light = new Light();
light.init();

这样子看起来非常完美。但是假如我们有三种光,甚至四种五种,只能在buttonWasPressed里面新增代码,这样就显得他很不稳定了。而且所有跟状态有关的行为,这里只是console,但是在实际应用中要处理的事情庞大得多...
所以,我们用状态模式进行改写

//OffLightState
var OffLightState = function(light) {
  this.light = light;
};
OffLightState.prototype.buttonWasPressed = function() {
  console.log('弱光'); 
  this.light.setState(this.light.weakLightState);
};

//WeakLightState
var WeakLightState = function(light) {
  this.light = light;
};
WeakLightState.prototype.buttonWasPressed = function() {
  console.log('强光');
  this.light.setState(this.light.strongLightState); 
};

//StrongLightState
var StrongLightState = function(light) {
  this.light = light;
};
StrongLightState.prototype.buttonWasPressed = function() {
  console.log('关灯');
  this.light.setState(this.light.OffLightState); 
};

var Light = function() {
  this.offLightState = new OffLightState(this);
  this.weakLightState = new WeakLightState(this);
  this.strongLightState = new StrongLightState(this);
};
Light.prototype.init = function() {
  var button = document.createElement('button'),
      self = this;
  this.button = document.body.appendChild(button);
  this.button.innerHTML = '开关';

  this.currState = this.offLightState; //设置当前状态
  this.button.onclick = function() {
    self.currState.buttonWasPressed();
  };
};

这样子,就没有了一堆繁复的if语句,如果再要增加什么状态,只需要新增对应的代码,再改变状态类之间的切换规则就好了。

状态模式的优缺点

  • 状态模式定义了状态与行为之间的关系,并将他们封装在一个类里。通过增加新的状态类,很容易增加新的状态和转换。

  • 避免context无线膨胀

  • 用对象代替字符串来记录当前状态使状态一目了然但是状态模式也会导致系统中定义许多的状态类,编写几十个状态类是一项枯燥的工作。虽然避开了不受欢迎的条件分支,但是也造成了逻辑分散的问题。