学习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 相关文章推荐
Prototype源码浅析 Enumerable部分(二)
Jan 18 Javascript
jquery解决图片路径不存在执行替换路径
Feb 06 Javascript
jquery中filter方法用法实例分析
Feb 06 Javascript
jQuery layui常用方法介绍
Jul 25 Javascript
Webpack 服务器端代码打包的示例代码
Sep 19 Javascript
如何选择适合你的JavaScript框架
Nov 20 Javascript
原生JS实现ajax与ajax的跨域请求实例
Dec 01 Javascript
vue + element-ui实现简洁的导入导出功能
Dec 22 Javascript
webpack手动配置React开发环境的步骤
Jul 02 Javascript
JavaScript中引用vs复制示例详析
Dec 06 Javascript
微信小程序使用自定义组件导航实现当前页面高亮
Jan 02 Javascript
vue图片裁剪插件vue-cropper使用方法详解
Dec 16 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
长波知识介绍
2021/03/01 无线电
php自动跳转中英文页面
2008/07/29 PHP
PHP实现生成唯一编号(36进制的不重复编号)
2014/07/01 PHP
CI框架中类的自动加载问题分析
2016/11/21 PHP
PHP使用PDO实现mysql防注入功能详解
2019/12/20 PHP
js中文逗号转英文实现
2014/02/11 Javascript
jQuery学习笔记之jQuery构建函数的7种方法
2014/06/03 Javascript
JavaScript及jquey实现多个数组的合并操作
2014/09/06 Javascript
分享12个实用的jQuery代码片段
2016/03/09 Javascript
用JS中split方法实现彩色文字背景效果实例
2016/08/24 Javascript
深入理解jQuery()方法的构建原理
2016/12/05 Javascript
使用JavaScript实现表格编辑器(实例讲解)
2017/08/02 Javascript
vue安装和使用scss及sass与scss的区别详解
2018/10/15 Javascript
微信小程序常用赋值方法小结
2019/04/30 Javascript
微信小程序实现卡片左右滑动效果的示例代码
2019/05/01 Javascript
JavaScript进阶(四)原型与原型链用法实例分析
2020/05/09 Javascript
vue监听浏览器原生返回按钮,进行路由转跳操作
2020/09/09 Javascript
python实现目录树生成示例
2014/03/28 Python
python中的内置函数max()和min()及mas()函数的高级用法
2018/03/29 Python
Python将一个Excel拆分为多个Excel
2018/11/07 Python
python3爬虫获取html内容及各属性值的方法
2018/12/17 Python
PYQT5设置textEdit自动滚屏的方法
2019/06/14 Python
Python Web框架之Django框架Form组件用法详解
2019/08/16 Python
python IDLE添加行号显示教程
2020/04/25 Python
python 通过exifread读取照片信息
2020/12/24 Python
css3发光搜索表单分享
2014/04/11 HTML / CSS
联想西班牙官网:Lenovo西班牙
2018/08/28 全球购物
Ootori在线按摩椅店:一家专业的按摩椅制造商
2019/04/10 全球购物
《埃及的金字塔》教学反思
2014/04/07 职场文书
公司活动总结范文
2014/07/01 职场文书
美术第二课堂活动总结
2014/07/08 职场文书
租房协议书
2014/09/12 职场文书
青岛海底世界导游词
2015/02/11 职场文书
公司开业致辞
2015/07/29 职场文书
Redis基本数据类型List常用操作命令
2022/06/01 Redis
Java中的Kafka为什么性能这么快及4大核心详析
2022/09/23 Java/Android