学习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 相关文章推荐
[原创]图片分页查看
Aug 28 Javascript
判断JS对象是否拥有某种属性的两种方式
Dec 02 Javascript
JavaScript自定义日期格式化函数详细解析
Jan 14 Javascript
使用伪命名空间封装保护独自创建的对象方法
Aug 04 Javascript
jQuery解决input元素的blur事件和其他非表单元素的click事件冲突问题
Aug 15 Javascript
微信小程序 教程之小程序配置
Oct 17 Javascript
在JSP中如何实现MD5加密的方法
Nov 02 Javascript
javascript基础练习之翻转字符串与回文
Feb 20 Javascript
详解JavaScript修改注册表的方法
Jan 05 Javascript
微信小程序自定义弹出模态框禁止底部滚动功能
Mar 09 Javascript
vue如何使用rem适配
Feb 06 Vue.js
Javascript设计模式之原型模式详细
Oct 05 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
PHP.MVC的模板标签系统(二)
2006/09/05 PHP
PHP基础知识回顾
2012/08/16 PHP
php使HTML标签自动补全闭合函数代码
2012/10/04 PHP
php htmlentities()函数的定义和用法
2016/05/13 PHP
总结的一些PHP开发中的tips(必看篇)
2017/03/24 PHP
PHP简单读取xml文件的方法示例
2017/04/20 PHP
PHP 传输会话curl函数的实例详解
2017/09/12 PHP
Jquery 设置标题的自动翻转
2009/10/03 Javascript
一个可绑定数据源的jQuery数据表格插件
2010/07/17 Javascript
jQuery.query.js 取参数的两点问题分析
2012/08/06 Javascript
如何用js控制frame的隐藏或显示的解决办法
2013/03/20 Javascript
js模仿jquery的写法示例代码
2013/06/16 Javascript
在服务端(Page.Write)调用自定义的JS方法详解
2013/08/09 Javascript
浅谈JavaScript数据类型
2015/03/03 Javascript
js获取地址栏中传递的参数(两种方法)
2017/02/08 Javascript
浅析vue深复制
2018/01/29 Javascript
浅谈VUE-CLI脚手架热更新太慢的原因和解决方法
2018/09/28 Javascript
Vue项目环境搭建详细总结
2019/09/26 Javascript
Vue实现多标签选择器
2019/11/28 Javascript
Vuex中的Mutations的具体使用方法
2020/06/01 Javascript
vant实现购物车功能
2020/06/29 Javascript
写了个监控nginx进程的Python脚本
2012/05/10 Python
python中matplotlib实现最小二乘法拟合的过程详解
2017/07/11 Python
Python自定义一个异常类的方法
2019/06/27 Python
windows上安装python3教程以及环境变量配置详解
2019/07/18 Python
基于python3实现倒叙字符串
2020/02/18 Python
python爬虫调度器用法及实例代码
2020/11/30 Python
英国屋顶用品和材料超市:Roofing Supplies UK
2019/08/24 全球购物
台湾三立电视电商平台:电电购
2019/09/09 全球购物
黑猩猩商店:The Chimp Store
2020/02/12 全球购物
C和C++经典笔试题附答案解析
2014/08/18 面试题
工厂厂长岗位职责
2013/11/08 职场文书
应急处置方案
2014/06/16 职场文书
nginx location优先级的深入讲解
2021/03/31 Servers
python numpy中setdiff1d的用法说明
2021/04/22 Python
彻底弄懂Python中的回调函数(callback)
2022/06/25 Python