学习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 相关文章推荐
javascript动画效果类封装代码
Aug 28 Javascript
海量经典的jQuery插件集合
Jan 12 Javascript
js中cookie的添加、取值、删除示例代码
Oct 21 Javascript
如何在指定的地方插入html内容和文本内容
Dec 23 Javascript
jquery预览图片实现鼠标放上去显示实际大小
Jan 16 Javascript
常用jQuery代码分享
Jul 14 Javascript
Bootstrap网格系统详解
Apr 26 Javascript
jQuery实现可以编辑的表格实例详解【附demo源码下载】
Jul 09 Javascript
浅谈javascript控制HTML5的全屏操控,浏览器兼容的问题
Oct 10 Javascript
Vue学习笔记进阶篇之函数化组件解析
Jul 21 Javascript
JavaScript动态添加数据到表单并提交的几种方式
Jun 26 Javascript
详解Vue中的watch和computed
Nov 09 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
全国FM电台频率大全 - 7 吉林省
2020/03/11 无线电
怎么样可以把 phpinfo()屏蔽掉?
2006/11/24 PHP
详解php的魔术方法__get()和__set()使用介绍
2012/09/19 PHP
php中的比较运算符详解
2013/10/28 PHP
ThinkPHP在新浪SAE平台的部署实例
2014/10/31 PHP
使用GDB调试PHP代码,解决PHP代码死循环问题
2015/03/02 PHP
php相对当前文件include其它文件的方法
2015/03/13 PHP
PHP实现的DES加密解密类定义与用法示例
2020/11/02 PHP
跟随鼠标旋转的文字
2006/11/30 Javascript
Use Word to Search for Files
2007/06/15 Javascript
jQuery+CSS实现菜单滑动伸展收缩(仿淘宝)
2013/03/22 Javascript
jquery简单实现鼠标经过导航条改变背景图
2013/12/17 Javascript
jQuery实现ajax调用WCF服务的方法(附带demo下载)
2015/12/04 Javascript
底部悬浮通栏可以关闭广告位的实现方法
2016/06/01 Javascript
JavaScript使用readAsDataUrl方法预览图片
2017/05/10 Javascript
VUE多层路由嵌套实现代码
2017/05/15 Javascript
jQuery实现标签子元素的添加和赋值方法
2018/02/24 jQuery
element el-input directive数字进行控制
2018/10/11 Javascript
Python版的文曲星猜数字游戏代码
2013/09/02 Python
python实现数值积分的Simpson方法实例分析
2015/06/05 Python
详解Python中 __get__和__getattr__和__getattribute__的区别
2016/06/16 Python
Python常见加密模块用法分析【MD5,sha,crypt模块】
2017/05/24 Python
Python中一些深不见底的“坑”
2019/06/12 Python
CSS3中新增的对文本和字体的设置
2020/02/03 HTML / CSS
廉价航班、机票和酒店:JustFly
2018/02/07 全球购物
Marlies Dekkers内衣荷兰官方网店:荷兰奢侈内衣品牌
2020/03/27 全球购物
C#可否对内存进行直接的操作
2015/02/26 面试题
小学庆六一活动方案
2014/02/28 职场文书
岗位廉政承诺书
2014/03/27 职场文书
我的梦中国梦演讲稿
2014/04/23 职场文书
创先争优标语
2014/06/27 职场文书
党员作风建设整改方案
2014/10/27 职场文书
2015年党员个人自我评价
2015/03/03 职场文书
中考百日冲刺决心书
2015/09/22 职场文书
使用springMVC所需要的pom配置
2021/09/15 Java/Android
Vue.js中v-bind指令的用法介绍
2022/03/13 Vue.js