js之状态模式
level01:电灯程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var Light = function(){
this.state = 'off'; // 给电灯设置初始状态 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(){
self.buttonWasPressed();
}
};
Light.prototype.buttonWasPressed = function(){
if ( this.state === 'off' ){
console.log( '开灯' );
this.state = 'on';
}else if ( this.state === 'on' ){
console.log( '关灯' );
this.state = 'off';
}
};
var light = new Light();
light.init();
</script>
</body>
</html>
缺点:
- 很明显 buttonWasPressed 方法是违反开放封闭原则的,每次新增或者修改 light 的状态,都需要改动buttonWasPressed 方法中的代码,这使得 buttonWasPressed 成为了一个非常不稳定的方法。
- 在状态多的情况下buttonWasPressed方法可能会很庞大。
- 状态之间的切换关系,不过是往 buttonWasPressed 方法里堆砌 if 、 else 语句,增加或者修改一个状态可能需要改变若干个操作,这使 buttonWasPressed 更加难以阅读和维护。
level02:状态模式改进电灯程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// OffLightState:
var OffLightState = function( light ){
this.light = light;
};
OffLightState.prototype.buttonWasPressed = function(){
console.log( '弱光' ); // offLightState 对应的行为
this.light.setState( this.light.weakLightState ); // 切换状态到 weakLightState
};
// WeakLightState:
var WeakLightState = function( light ){
this.light = light;
};
WeakLightState.prototype.buttonWasPressed = function(){
console.log( '强光' ); // weakLightState 对应的行为
this.light.setState( this.light.strongLightState ); // 切换状态到 strongLightState
};
// StrongLightState:
var StrongLightState = function( light ){
this.light = light;
};
StrongLightState.prototype.buttonWasPressed = function(){
console.log( '关灯' ); // strongLightState 对应的行为
this.light.setState( this.light.offLightState ); // 切换状态到 offLightState
};
var Light = function(){
this.offLightState = new OffLightState( this );
this.weakLightState = new WeakLightState( this );
this.strongLightState = 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.currState = this.offLightState; // 设置当前状态
this.button.onclick = function(){
self.currState.buttonWasPressed();
}
};
Light.prototype.setState = function( newState ){
this.currState = newState;
}; var light = new Light();
light.init();
</script>
</body>
</html>
tips:
在前面的电灯例子中,我们完成了一个状态模式程序的编写。首先定义了 Light 类, Light类在这里也被称为上下文(Context)。随后在 Light 的构造函数中,我们要创建每一个状态类的实例对象,Context 将持有这些状态对象的引用,以便把请求委托给状态对象。
我们要编写各种状态类, light 对象被传入状态类的构造函数,状态对象也需要持有 light 对象的引用,以便调用 light 中的方法或者直接操作 light 对象:
var OffLightState = function( light ){
this.light = light;
};
OffLightState.prototype.buttonWasPressed = function(){
console.log( '弱光' );
this.light.setState( this.light.weakLightState );
};
状态模式的定义:
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
我们以逗号分割,把这句话分为两部分来看。第一部分的意思是将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不同的行为变化。电灯的例子足以说明这一点,在 off和 on这两种不同的状态下,我们点击同一个按钮,得到的行为反馈是截然不同的。
level03:状态模式改进电灯程序
憾的是,JavaScript既不支持抽象类,也没有接口的概念。所以在使用状态模式的时候要格外小心,如果我们编写一个状态子
类时,忘记了给这个状态子类实现 buttonWasPressed 方法,则会在状态切换的时候抛出异常。因为 Context总是把请求委托给状态对象的 buttonWasPressed 方法。
不论怎样严格要求程序员,也许都避免不了犯错的那一天,毕竟如果没有编译器的帮助,只依靠程序员的自觉以及一点好运气,是不靠谱的。这里建议的解决方案跟《模板方法模式》中一致,让抽象父类的抽象方法直接抛出一个异常,这个异常至少会在程序运行期间就被发现:
var State = function(){};
State.prototype.buttonWasPressed = function(){
throw new Error( '父类的 buttonWasPressed 方法必须被重写' );
};
var SuperStrongLightState = function( light ){
this.light = light;
};
SuperStrongLightState.prototype = new State(); // 继承抽象父类
SuperStrongLightState.prototype.buttonWasPressed = function(){ // 重写 buttonWasPressed 方法
console.log( '关灯' );
this.light.setState( this.light.offLightState );
};
状态模式的优缺点
- 状态模式定义了状态与行为之间的关系,并将它们封装在一个类里。通过增加新的状态类,很容易增加新的状态和转换。
- 避免 Context 无限膨胀,状态切换的逻辑被分布在状态类中,也去掉了 Context 中原本过多的条件分支。
- Context中的请求动作和状态类中封装的行为可以非常容易地独立变化而互不影响。
状态模式中的性能优化点
- 有两种选择来管理 state 对象的创建和销毁。第一种是仅当 state 对象被需要时才创建并随后销毁,另一种是一开始就创建好所有的状态对象,并且始终不销毁它们。如果 state对象比较庞大,可以用第一种方式来节省内存,这样可以避免创建一些不会用到的对象并及时地回收它们。但如果状态的改变很频繁,最好一开始就把这些 state 对象都创建出来,也没有必要销毁它们,因为可能很快将再次用到它们。
- 在本章的例子中,我们为每个 Context 对象都创建了一组 state 对象,实际上这些 state对象之间是可以共享的,各Context 对象可以共享一个 state 对象,这也是享元模式的应
用场景之一。
状态模式和策略模式的关系
状态模式和策略模式像一对双胞胎,它们都封装了一系列的算法或者行为,它们的类图看起来几乎一模一样,但在意图上有很大不同,因此它们是两种迥然不同的模式。
策略模式和状态模式的相同点是,它们都有一个上下文、一些策略或者状态类,上下文把请求委托给这些类来执行。
它们之间的区别是策略模式中的各个策略类之间是平等又平行的,它们之间没有任何联系,所以客户必须熟知这些策略类的作用,以便客户可以随时主动切换算法;而在状态模式中,状态和状态对应的行为是早已被封装好的,状态之间的切换也早被规定完成,“改变行为”这件事情发生在状态模式内部。对客户来说,并不需要了解这些细节。这正是状态模式的作用所在。
level02:JavaScript 版本的状态机
状态模式是状态机的实现之一,但在 JavaScript这种“无类”语言中,没有规定让状态对象一定要从类中创建而来。另外一点,JavaScript 可以非常方便地使用委托技术,并不需要事先让一个对象持有另一个对象。下面的状态机选择了通过Function.prototype.call 方法直接把请求委托给某个字面量对象来执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var Light = function(){
this.button = null;
};
Light.prototype.init = function(){
var button = document.createElement( 'button' ),
self = this;
button.innerHTML = '已关灯';
this.currState = FSM.off; // 设置当前状态
this.button = document.body.appendChild( button );
this.button.onclick = function(){
self.currState.buttonWasPressed.call( self ); // 把请求委托给 FSM 状态机
}
};
var FSM = {
off: {
buttonWasPressed: function(){
console.log( '关灯' );
this.button.innerHTML = '下一次按我是开灯';
this.currState = FSM.on;
}
},
on: {
buttonWasPressed: function(){
console.log( '开灯' );
this.button.innerHTML = '下一次按我是关灯';
this.currState = FSM.off;
}
}
};
var light = new Light();
light.init();
</script>
</body> </html>
js之状态模式的更多相关文章
- js 设计模式——状态模式
状态模式 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类. 简单的解释一下: 第一部分的意思是将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来 ...
- js设计模式——5.状态模式
js设计模式——5.状态模式 代码演示 /*js设计模式——状态模式*/ // 状态(红灯,黄灯,绿灯) class State { constructor(color) { this.color = ...
- js中State模式的解析及运用
状态模式,在大的范畴中的定义为当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类.每种编程语言有不同的实现方式,运用的范围也多用于游戏之中. 这里我用javascript来模拟状 ...
- JS常用的设计模式(17)—— 状态模式
状态模式主要可以用于这种场景 1 一个对象的行为取决于它的状态 2 一个操作中含有庞大的条件分支语句 回想下街头霸王的游戏. 隆有走动,攻击,防御,跌倒,跳跃等等多种状态,而这些状态之间既有联系又互相 ...
- js状态模式
状态模式,当一个对象的内在状态改变时允许改变其行为,这个对象看起来是改变了其类. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类当 ...
- js策略模式vs状态模式
一.策略模式 1.定义:把一些小的算法,封装起来,使他们之间可以相互替换(把代码的实现和使用分离开来)2.利用策略模式实现小方块缓动 html代码: <div id="containe ...
- 8.js模式-状态模式
1. 状态模式 var offLightState = function(light){ this.light = light; } offLightState.prototype.buttonWas ...
- 大熊君说说JS与设计模式之------状态模式State
一,总体概要 1,笔者浅谈 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式. 状态模式主要解决的是当控制一个对象状态的条件表达式过于 ...
- JS设计模式(13)状态模式
什么是状态模式? 定义:将事物内部的每个状态分别封装成类,内部状态改变会产生不同行为. 主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为. 何时使用:代码中包含大 ...
随机推荐
- dll和ocx的区别
ActiveX,OLE是基于COM的一种应用,其文件后缀一般以dll和ocx结尾:ocx作为一种特殊的dll文件,具有一定的用户界面和事件响应,而dll文件只是方法和属性的集合. 一.关于DLL的介绍 ...
- VS 2012 Unit Test
1,Open Tool->Custmoize 2,Create Unit Tests Move Down Run Test 3,Restart run VS 4,Create UnitTest ...
- 图像语义分割出的json文件和原图,用plt绘制图像mask
1.弱监督 由于公司最近准备开个新项目,用深度学习训练个能够自动标注的模型,但模型要求的训练集比较麻烦,,要先用ffmpeg从视频中截取一段视频,在用opencv抽帧得到图片,所以本人只能先用语义分割 ...
- quartz 时间配置
Quartz中时间表达式的设置-----corn表达式 (注:这是让我看比较明白的一个博文,但是抱歉,没有找到原作者,如有侵犯,请告知) 时间格式: <!-- s m h d m w(?) y( ...
- ARM指令集的最新版本包括针对JavaScript的优化
在ARM指令集中,ARMv8.3添加了一个新的float-to-int指令,其错误和超出范围的值按照JavaScript的方式处理.以前[指令]获取JavaScript的语义要慢得多,JavaScri ...
- java:类集框架conllection接口list,set
类集中提供了以下几种接口: 1.单值操作接口:conllection,List,Set list和set是conllection接口的子接口 2.一对值的操作接口:Map 3.排序的操作接口:Sort ...
- Linux kswapd0 进程CPU占用过高
图便宜买了个1核1G虚拟机,启动两个jar后cpu飙升直接卡死,查看cpu及内存占用 发现kswapd0进程cpu占用一直居高不下,于是查询资料,总结如下. swap分区的作用是当物理内存不足时,会将 ...
- layui数据表格排序图标被超出的表头挤出去
如果表头过长,会出现超出显示三个省略号,然后把排序图标挤出去,看不到了, 效果如下 解决办法就是给图标加定位,过长的时候加上 .show-sort{ position: absolute; right ...
- HTML和CSS遇到的细节问题
一.列表项标记窜出div盒子 列表项标记窜出盒子,是因为设置了 *; } ,消除了<li>元素的默认外边距. 结解决方法:消除*{}选择器或是设置外边距 列表项目标记与边距有关 二.div ...
- jquery ajax实现文件上传
test5.html <html> <head> <meta http-equiv="Content-Type" content="text ...