学习JavaScript设计模式之状态模式


Posted in Javascript onJanuary 08, 2016

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

当电灯开着,此时按下开关,电灯会切换到关闭状态;再按一次开关,电灯又将被打开。同一个开关在不同的状态下,表现出来的行为是不一样的。

一、有限状态机

  • 状态总数(state)是有限的。
  • 任一时刻,只处在一种状态之中。
  • 某种条件下,会从一种状态转变(transition)到另一种状态。

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
解释:
(1)将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态发生改变时,会带来不同的行为变化。
(2)使用的对象,在不同的状态下具有截然不同的行为(委托效果)

谈到封装,一般优先考虑封装对象的行为,而不是对象的状态。
但在状态模式中刚好相反,状态模式的关键是把事物的每种状态都封装成单独的类。

二、示例

点灯程序 (弱光 ?> 强光 ?> 关灯)循环

// 关灯
var OffLightState = function(light) {
  this.light = light;
};
// 弱光
var OffLightState = function(light) {
  this.light = light;
};
// 强光
var StrongLightState = function(light) {
  this.light = light;
};

var Light = function(){
  /* 开关状态 */
  this.offLight = new OffLightState(this);
  this.weakLight = new WeakLightState(this);
  this.strongLight = new StrongLightState(this);
  /* 快关按钮 */
  this.button = null;
};
Light.prototype.init = function() {
  var button = document.createElement("button"),
    self = this;
  this.button = document.body.appendChild(button);
  this.button.innerHTML = '开关';
  this.currentState = this.offLight;
  this.button.click = function() {
    self.currentState.buttonWasPressed();
  }
};
// 让抽象父类的抽象方法直接抛出一个异常(避免状态子类未实现buttonWasPressed方法)
Light.prototype.buttonWasPressed = function() {
  throw new Error("父类的buttonWasPressed方法必须被重写");
};
Light.prototype.setState = function(newState) {
  this.currentState = newState;
};

/* 关灯 */
OffLightState.prototype = new Light(); // 继承抽象类
OffLightState.prototype.buttonWasPressed = function() {
  console.log("关灯!");
  this.light.setState(this.light.weakLight);
}
/* 弱光 */
WeakLightState.prototype = new Light();
WeakLightState.prototype.buttonWasPressed = function() {
  console.log("弱光!");
  this.light.setState(this.light.strongLight);
};
/* 强光 */
StrongLightState.prototype = new Light();
StrongLightState.prototype.buttonWasPressed = function() {
  console.log("强光!");
  this.light.setState(this.light.offLight);
};

PS:说明补充
必须把OffLightState、WeakLightState、StrongLightState构造函数提前。

new A("a");
var A = function(a) {
  console.log(a)
}

new B("b");
function B(b) {
  console.log(b);
}

函数声明会被提升到普通变量之前。

三、性能优化点

(1)如何管理状态对象的创建和销毁?
第一种仅当state对象被需要时才创建并随后销毁(state对象比较庞大,优先选择),
另一种是一开始就创建好所有的状态对象,并且始终不销毁它们(状态改变频繁)。
(2)利用享元模式共享一个state对象。

四、JavaScript版本的状态机

(1)通过Function.prototype.call方法直接把请求委托给某个字面量对象来执行

// 状态机
var FSM = {
  off: {
    buttonWasPressed: function() {
      console.log("关灯");
      this.button.innerHTML = "下一次按我是开灯";   // 这是Light上的属性!!!
      this.currState = FSM.on;            // 这是Light上的属性!!!
    }
  },
  on: {
    buttonWasPressed: function() {
      console.log("开灯");
      this.button.innerHTML = "下一次按我是关灯";
      this.currState = FSM.off;
    }
  },
};

var Light = function() {
  this.currState = FSM.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() {
    // 请求委托给FSM状态机
    self.currState.buttonWasPressed.call(self);
  }

}

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

(2)利用delegate函数

var delegate = function(client, delegation) {
  return {
    buttonWasPressed: function() {
      return delegation.buttonWasPressed.apply(client, arguments);
    }
  };
};

// 状态机
var FSM = {
  off: {
    buttonWasPressed: function() {
      console.log("关灯");
      this.button.innerHTML = "下一次按我是开灯";
      this.currState = this.onState;
    }
  },
  on: {
    buttonWasPressed: function() {
      console.log("开灯");
      this.button.innerHTML = "下一次按我是关灯";
      this.currState = this.offState;
    }
  },
};

var Light = function() {
  this.offState = delegate(this, FSM.off);
  this.onState = delegate(this, FSM.on);
  this.currState = this.offState; // 设置当前状态
  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() {
    // 请求委托给FSM状态机
    self.currState.buttonWasPressed();
  }
}

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

希望本文所述对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
基于jquery点击自以外任意处,关闭自身的代码
Feb 10 Javascript
checkbox选中与未选中判断示例
Aug 04 Javascript
JavaScript中检查对象property的存在性方法介绍
Dec 30 Javascript
js实现在网页上简单显示时间的方法
Mar 02 Javascript
AngularJS入门教程之迭代器过滤详解
Aug 18 Javascript
JS匿名函数类生成方式实例分析
Nov 26 Javascript
vue2.0实现分页组件的实例代码
Jun 22 Javascript
微信小程序背景音乐开发详解
Dec 12 Javascript
基于vue的tab-list类目切换商品列表组件的示例代码
Feb 14 Javascript
JS 逻辑判断不要只知道用 if-else 和 switch条件判断(小技巧)
May 27 Javascript
js实现石头剪刀布游戏
Oct 11 Javascript
vue如何在data中引入图片的正确路径
Jun 05 Vue.js
jQuery CSS3相结合实现时钟插件
Jan 08 #Javascript
js实现对ajax请求面向对象的封装
Jan 08 #Javascript
javascript弹性运动效果简单实现方法
Jan 08 #Javascript
javascript运动效果实例总结(放大缩小、滑动淡入、滚动)
Jan 08 #Javascript
javascript运动框架用法实例分析(实现放大与缩小效果)
Jan 08 #Javascript
jquery实现简单的遮罩层
Jan 08 #Javascript
javascript多物体运动实现方法分析
Jan 08 #Javascript
You might like
ThinkPHP3.1新特性之对分组支持的改进与完善概述
2014/06/19 PHP
php自定义错误处理用法实例
2015/03/20 PHP
PHP+Apache+Mysql环境搭建教程
2016/08/01 PHP
php报错502badgateway解决方法
2019/10/11 PHP
jQuery select的操作实现代码
2009/05/06 Javascript
基于jquery DOM写的类似微博发布的效果
2012/10/20 Javascript
浅析js封装和作用域
2013/07/09 Javascript
JavaScript给input的value赋值引发的关于基本类型值和引用类型值问题
2015/12/07 Javascript
js和jq使用submit方法无法提交表单的快速解决方法
2016/05/17 Javascript
js添加千分位的实现代码(超简单)
2016/08/01 Javascript
手把手教你搭建ES6的开发运行环境
2017/07/11 Javascript
angularJS自定义directive之带参方法传递详解
2018/10/09 Javascript
Vue.js轮播图走马灯代码实例(全)
2019/05/08 Javascript
Vue+Django项目部署详解
2019/05/30 Javascript
vue实现移动端项目多行文本溢出省略
2020/07/29 Javascript
JavaScript 判断数据类型的4种方法
2020/09/11 Javascript
Python列表(list)常用操作方法小结
2015/02/02 Python
15行Python代码带你轻松理解令牌桶算法
2018/03/21 Python
儿童python练习实例
2018/05/27 Python
Python 查找list中的某个元素的所有的下标方法
2018/06/27 Python
Window 64位下python3.6.2环境搭建图文教程
2018/09/19 Python
解决Python中定时任务线程无法自动退出的问题
2019/02/18 Python
Django自定义模板过滤器和标签的实现方法
2019/08/21 Python
python opencv进行图像拼接
2020/03/27 Python
Python爬取12306车次信息代码详解
2020/08/12 Python
CSS3中的@keyframes关键帧动画的选择器绑定
2016/06/13 HTML / CSS
HTML5 canvas基本绘图之绘制线条
2016/06/27 HTML / CSS
跑步爱好者一站式服务网站:Jack Rabbit
2016/09/01 全球购物
澳大利亚领先的时尚内衣零售商:Bras N Things
2020/07/28 全球购物
干部作风建设心得体会
2014/10/22 职场文书
2014年村官工作总结
2014/11/24 职场文书
怎样写家长意见
2015/06/04 职场文书
2015年治庸问责工作总结
2015/07/27 职场文书
2016年三八红旗手先进事迹材料
2016/02/26 职场文书
CSS 圆形进度栏
2021/04/06 HTML / CSS
React-vscode使用jsx语法的问题及解决方法
2021/06/21 Javascript