学习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 无废话系列教程(二) jquery实战篇上
Jun 23 Javascript
javascript 动态生成私有变量访问器
Dec 06 Javascript
JQuery的ajax获取数据后的处理总结(html,xml,json)
Jul 14 Javascript
jQuery之Deferred对象详解
Sep 04 Javascript
jQuery $命名冲突解决方案汇总
Nov 13 Javascript
浅谈JavaScript数据类型
Mar 03 Javascript
在Javascript中处理数组之toSource()方法的使用
Jun 09 Javascript
常用JS图片滚动(无缝、平滑、上下左右滚动)代码大全(推荐)
Dec 20 Javascript
iView框架问题整理小结
Oct 16 Javascript
Vue 微信端扫描二维码苹果端却只能保存图片问题(解决方法)
Jan 19 Javascript
JavaScript鼠标拖拽事件详解
Apr 03 Javascript
小程序实现点击tab切换左右滑动
Nov 16 Javascript
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
一个MYSQL操作类
2006/11/16 PHP
PHP中的随机性 你觉得自己幸运吗?
2016/01/22 PHP
浅谈Laravel中的一个后期静态绑定
2017/08/11 PHP
利用PHP如何统计Nginx日志的User Agent数据
2019/03/06 PHP
php快速导入大量数据的实例方法
2019/09/23 PHP
JavaScript脚本性能优化注意事项
2008/11/18 Javascript
含有CKEditor的表单如何提交
2014/01/09 Javascript
多种方法实现load加载完成后把图片一次性显示出来
2014/02/19 Javascript
jQuery中has()方法用法实例
2015/01/06 Javascript
Bootstrap4一次重大更新 几乎涉及每行代码
2016/05/16 Javascript
angularjs实现的前端分页控件示例
2017/02/10 Javascript
js实现移动端微信页面禁止字体放大
2017/02/16 Javascript
jquery动态添加带有样式的HTML标签元素方法
2018/02/24 jQuery
用POSTMAN发送JSON格式的POST请求示例
2018/09/04 Javascript
vue在图片上传的时候压缩图片
2020/11/18 Vue.js
用Python中的字典来处理索引统计的方法
2015/05/05 Python
在SAE上部署Python的Django框架的一些问题汇总
2015/05/30 Python
python获取mp3文件信息的方法
2015/06/15 Python
pycharm安装和首次使用教程
2018/08/27 Python
PyCharm+Qt Designer+PyUIC安装配置教程详解
2019/06/13 Python
python爬虫之自制英汉字典
2019/06/24 Python
Matplotlib绘制雷达图和三维图的示例代码
2020/01/07 Python
python如何爬取动态网站
2020/09/09 Python
python实现网页录音效果
2020/10/26 Python
python二维图制作的实例代码
2020/12/03 Python
软件工程专业推荐信
2013/10/28 职场文书
社会实践心得体会
2014/01/03 职场文书
大学生自我评价范文分享
2014/02/21 职场文书
运动会演讲稿
2014/05/07 职场文书
松材线虫病防治方案
2014/06/15 职场文书
国际经济与贸易专业求职信
2014/07/10 职场文书
仓库统计员岗位职责
2015/04/14 职场文书
公司放假通知怎么写
2015/04/15 职场文书
解决pytorch 损失函数中输入输出不匹配的问题
2021/06/05 Python
Win11如何设置右键单击显示所有选项?Win11右键单击显示所有选项设置教程
2022/04/08 数码科技
postgresql如何找到表中重复数据的行并删除
2023/05/08 MySQL