学习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 ajax 调用带参数的webservice返回XML数据一个小细节
Jul 31 Javascript
基于mouseout和mouseover等类似事件的冒泡问题解决方法
Nov 18 Javascript
javascript对JSON数据排序的3个例子
Apr 12 Javascript
javascript学习笔记之10个原生技巧
May 21 Javascript
jquery获取及设置outerhtml的方法
Mar 09 Javascript
javascript排序函数实现数字排序
Jun 26 Javascript
javascript中undefined与null的区别
Aug 16 Javascript
BootStrap中Tab页签切换实例代码
May 30 Javascript
用Vue-cli搭建的项目中引入css报错的原因分析
Jul 20 Javascript
vue实现某元素吸顶或固定位置显示(监听滚动事件)
Dec 13 Javascript
JS如何把字符串转换成json
Feb 21 Javascript
mapboxgl实现带箭头轨迹线的代码
Jan 04 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
如何选购合适的收音机
2021/03/01 无线电
PHP数组 为文章加关键字连接 文章内容自动加链接
2011/12/29 PHP
php使用strtotime和date函数判断日期是否有效代码分享
2013/12/25 PHP
php导入csv文件碰到乱码问题的解决方法
2014/02/10 PHP
详解WordPress中添加友情链接的方法
2016/05/21 PHP
Yii2针对游客、用户防范规则和限制的解决方法分析
2016/10/08 PHP
基于JQuery实现异步刷新的代码(转载)
2011/03/29 Javascript
JS执行删除前的判断代码
2014/02/18 Javascript
jQuery实现购物车多物品数量的加减+总价计算
2014/06/06 Javascript
javascript遇到html5的一些表单属性
2015/07/05 Javascript
JS+CSS实现TreeMenu二级树形菜单完整实例
2015/09/18 Javascript
JavaScript学习笔记之数组去重
2016/03/23 Javascript
JavaScript中的Object对象学习教程
2016/05/20 Javascript
EsLint入门学习教程
2017/02/17 Javascript
微信小程序 http请求的session管理
2017/06/07 Javascript
微信小程序自定义波浪组件使用方法详解
2019/09/21 Javascript
[15:35]教你分分钟做大人:天怒法师
2014/10/30 DOTA
如何在sae中设置django,让sae的工作环境跟本地python环境一致
2017/11/21 Python
Python PyQt5模块实现窗口GUI界面代码实例
2020/05/12 Python
Python用requests库爬取返回为空的解决办法
2021/02/21 Python
ROSEFIELD手表荷兰官方网上商店:北欧极简设计女士腕表品牌
2018/01/24 全球购物
乐高西班牙官方商店:LEGO Shop ES
2019/12/01 全球购物
自我鉴定的范文
2013/10/03 职场文书
参观考察邀请函范文
2014/01/29 职场文书
庆祝教师节活动方案
2014/01/31 职场文书
一名老师的自我评价
2014/02/07 职场文书
《真想变成大大的荷叶》教学反思
2014/04/14 职场文书
协议书怎么写
2014/04/21 职场文书
社区工作者演讲稿
2014/05/23 职场文书
学雷锋先进个人事迹
2014/05/26 职场文书
标准毕业生自荐信
2014/06/24 职场文书
企业整改报告范文
2014/11/08 职场文书
英文产品推荐信
2015/03/27 职场文书
2016高校自主招生自荐信范文
2016/01/28 职场文书
关于拾金不昧的感谢信(五篇)
2019/10/18 职场文书
mybatis源码解读之executor包语句处理功能
2022/02/15 Java/Android