学习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 07 Javascript
javascript随机之洗牌算法深入分析
Jun 07 Javascript
JS网页在线获取鼠标坐标值的方法
Feb 28 Javascript
跟我学习javascript的循环
Nov 18 Javascript
深入理解JS中的Function.prototype.bind()方法
Oct 11 Javascript
jQuery 如何实现一个滑动按钮开关
Dec 01 Javascript
微信小程序实现MUI数字输入框效果
Jan 31 Javascript
微信小程序云开发实现增删改查功能
May 17 Javascript
JavaScript常用工具函数大全
May 06 Javascript
Element中Slider滑块的具体使用
Jul 29 Javascript
react ant Design手动设置表单的值操作
Oct 31 Javascript
关于element的表单组件整理笔记
Feb 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最常用的2种设计模式工厂模式和单例模式介绍
2012/08/14 PHP
PHP 获取文件路径(灵活应用__FILE__)
2013/02/15 PHP
实现PHP搜索加分页
2016/10/12 PHP
phpcms中的评论样式修改方法
2016/10/21 PHP
PHP Header用于页面跳转时的几个注意事项
2016/10/21 PHP
关于php支持的协议与封装协议总结(推荐)
2017/11/17 PHP
对jQuary选择器的全面总结
2016/06/20 Javascript
AngularJS指令与控制器之间的交互功能示例
2016/12/14 Javascript
angular2中router路由跳转navigate的使用与刷新页面问题详解
2017/05/07 Javascript
Node.js服务器开启Gzip压缩教程
2017/08/11 Javascript
详解10分钟学会vue滚动行为
2017/09/21 Javascript
js截取字符串功能的实现方法
2017/09/27 Javascript
JavaScript实现多叉树的递归遍历和非递归遍历算法操作示例
2018/02/08 Javascript
JavaScript实现的拼图算法分析
2019/02/13 Javascript
layui的面包屑或者表单不显示的解决方法
2019/09/05 Javascript
vue计算属性无法监听到数组内部变化的解决方案
2019/11/06 Javascript
Js和VUE实现跑马灯效果
2020/05/25 Javascript
[05:59]带你看看DPC的台前幕后
2021/03/11 DOTA
python中遍历文件的3个方法
2014/09/02 Python
Python实现将罗马数字转换成普通阿拉伯数字的方法
2017/04/19 Python
python实现list元素按关键字相加减的方法示例
2017/06/09 Python
用python结合jieba和wordcloud实现词云效果
2017/09/05 Python
Python cookbook(数据结构与算法)将序列分解为单独变量的方法
2018/02/13 Python
pycharm配置pyqt5-tools开发环境的方法步骤
2019/02/11 Python
Python 装饰器原理、定义与用法详解
2019/12/07 Python
python2 对excel表格操作完整示例
2020/02/23 Python
Python生成pdf目录书签的实例方法
2020/10/29 Python
python输出国际象棋棋盘的实例分享
2020/11/26 Python
Html5 localStorage入门教程
2018/04/26 HTML / CSS
三星新西兰官网:Samsung新西兰
2019/03/05 全球购物
请用用Java代码写一个堆栈
2012/01/26 面试题
初一地理教学反思
2014/01/16 职场文书
安全承诺书范文
2014/03/26 职场文书
2014年销售助理工作总结
2014/12/01 职场文书
Django cookie和session的应用场景及如何使用
2021/04/29 Python
简单了解 MySQL 中相关的锁
2021/05/25 MySQL