JavaScript设计模式(策略模式)
策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。将不变的部分和变化的部分隔开是每个设计模式的主题,策略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来。
一、最初的策略模式:
var calculateBonus = function( performanceLevel, salary ){
if ( performanceLevel === 'S' ){
return salary * 4;
}
if ( performanceLevel === 'A' ){
return salary * 3;
}
if ( performanceLevel === 'B' ){
return salary * 2;
}
};
calculateBonus( 'B', 20000 ); // 输出:40000 calculateBonus( 'S', 6000 ); // 输出:24000
或者使用组合函数封装改善下代码
var performanceS = function( salary ){
return salary * 4;
};
var performanceA = function( salary ){
return salary * 3;
};
var performanceB = function( salary ){
return salary * 2;
};
var calculateBonus = function( performanceLevel, salary ){
if ( performanceLevel === 'S' ){
return performanceS( salary );
}
if ( performanceLevel === 'A' ){
return performanceA( salary );
}
if ( performanceLevel === 'B' ){
return performanceB( salary );
}
} calculateBonus( 'A' , 10000 ); // 输出:30000
但是,这种改善非常有限,calculateBonus 函数有可能越来越庞大,而且在系统变化的时候缺乏弹性。
二、JavaScript 版本的策略模式:
在 JavaScript 语言中,函数也是对象,所以更简单和直接的做法是把 strategy 直接定义为函数
var strategies = {
"S": function( salary ){
return salary * 4; },
"A": function( salary ){
return salary * 3;
},
"B": function( salary ){
return salary * 2;
}
};
var calculateBonus = function( level, salary ){
return strategies[ level ]( salary );
};
console.log( calculateBonus( 'S', 20000 ) ); // 输出:80000
console.log( calculateBonus( 'A', 10000 ) ); // 输出:30000
三、使用策略模式重构表单:
1、初始的表单验证的写法:
<html>
<body>
<form action="http:// xxx.com/register" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/ >
请输入密码:<input type="text" name="password"/ >
请输入手机号码:<input type="text" name="phoneNumber"/ >
<button>提交</button>
</form>
<script type="text/javascript">
var registerForm = document.getElementById( 'registerForm' );
registerForm.onsubmit = function(){
if ( registerForm.userName.value === '' ){
alert ( '用户名不能为空' );
return false;
}
if ( registerForm.password.value.length < 6 ){ alert ( '密码长度不能少于 6 位' );
return false;
}
if ( !/(^1[3|5|8][0-9]{9}$)/.test( registerForm.phoneNumber.value ) ){
alert ( '手机号码格式不正确' );
return false;
}
}
</script>
</body>
</html>
以上这种写法有一些缺点:
(1)registerForm.onsubmit 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有的校验规则。
(2)registerForm.onsubmit 函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长 度校验从 6 改成 8,我们都必须深入 registerForm.onsubmit 函数的内部实现,这是违反开放—封闭原则的。
(3)算法的复用性差,如果在程序中增加了另外一个表单,这个表单也需要进行一些类似的 校验,那我们很可能将这些校验逻辑复制得漫天遍野。
2、使用策略模式重构之后的代码:
(1)第一步我们要把这些校验逻辑都封装成策略对象
var strategies = {
isNonEmpty: function( value, errorMsg ){
if ( value === '' ){
return errorMsg; // 不为空
}
},
minLength: function( value, length, errorMsg ){
if ( value.length < length ){
return errorMsg; // 限制最小长度
}
},
isMobile: function( value, errorMsg ){
if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){
return errorMsg; // 手机号码格式
}
}
};
(2)实现 Validator 类。Validator 类在这里作为 Context,负责接收用户的请求并委托给 strategy 对象。
var Validator = function(){
this.cache = []; // 保存校验规则
}; Validator.prototype.add = function(dom, rule, errorMsg ) {
var ary = rule.split( ':' ); // 把strategy和参数分开
this.cache.push(function(){ // 把校验的步骤用空函数包装起来,并且放入 cache
var strategy = ary.shift(); // 用户挑选的 strategy
ary.unshift( dom.value ); // 把 input 的 value 添加进参数列表
ary.push( errorMsg ); // 把 errorMsg 添加进参数列表
return strategies[strategy].apply( dom, ary );
});
};
Validator.prototype.start = function(){
for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息 if ( msg ){
if ( msg ){
return msg; // 如果有确切的返回值,说明校验没有通过
}
}
};
(3)用户使用代码:
var validataFunc = function(){
var validator = new Validator(); // 创建一个 validator 对象
/***************添加一些校验规则****************/
validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空' );
validator.add( registerForm.password, 'minLength:6', '密码长度不能少于 6 位' );
validator.add( registerForm.phoneNumber, 'isMobile', '手机号码格式不正确' );
var errorMsg = validator.start(); // 获得校验结果
return errorMsg; // 返回校验结果
}
var registerForm = document.getElementById( 'registerForm' );
registerForm.onsubmit = function(){
var errorMsg = validataFunc(); // 如果errorMsg有确切的返回值,说明未通过校验
if ( errorMsg ){
alert ( errorMsg );
return false; // 阻止表单提交 }
}
};
使用策略模式重构代码之后,我们仅仅通过“配置”的方式就可以完成一个表单的校验, 这些校验规则也可以复用在程序的任何地方,还能作为插件的形式,方便地被移植到其他项目中。
在修改某个校验规则的时候,只需要编写或者改写少量的代码。比如我们想将用户名输入框 的校验规则改成用户名不能少于 4 个字符。可以看到,这时候的修改是毫不费力的。代码如下:
validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空' ); // 改成:
validator.add( registerForm.userName, 'minLength:10', '用户名长度不能小于 10 位' );
四、给某个文本输入框添加多种校验规则
<html>
<body>
<form action="http:// xxx.com/register" id="registerForm" method="post"> 请输入用户名:<input type="text" name="userName"/ > 请输入密码:<input type="text" name="password"/ > 请输入手机号码:<input type="text" name="phoneNumber"/ > <button>提交</button>
</form>
<script> /***********************策略对象**************************/
var strategies = {
isNonEmpty: function( value, errorMsg ){
if ( value === '' ){
return errorMsg;
}
},
minLength: function( value, length, errorMsg ){
if ( value.length < length ){
return errorMsg;
}
},
isMobile: function( value, errorMsg ){
if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){
return errorMsg;
}
}
}; /***********************Validator 类**************************/
var Validator = function(){
this.cache = [];
};
Validator.prototype.add = function( dom, rules ){
var self = this;
for ( var i = 0, rule; rule = rules[ i++ ]; ){
(function(rule){
var strategyAry = rule.strategy.split( ':' ); var errorMsg = rule.errorMsg;
self.cache.push(function(){
var strategy = strategyAry.shift();
strategyAry.push( errorMsg );
return strategies[ strategy ].apply( dom, strategyAry );
});
})(rule)
}
};
Validator.prototype.start = function(){
for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
var errorMsg = validatorFunc();
if ( errorMsg ){
return errorMsg;
}
}
}; /***********************客户调用代码**************************/
var registerForm = document.getElementById( 'registerForm' );
var validataFunc = function() {
var validator = new Validator();
validator.add( registerForm.userName, [{ strategy: 'isNonEmpty', errorMsg: '用户名不能为空'},
{strategy: 'minLength:6', errorMsg: '用户名长度不能小于 10 位'}]); validator.add( registerForm.password, [{strategy: 'minLength:6',errorMsg: '密码长度不能小于 6 位'}]); validator.add( registerForm.phoneNumber, [{ strategy: 'isMobile',errorMsg: '手机号码格式不正确'}]); var errorMsg = validator.start();
return errorMsg;
}
registerForm.onsubmit = function(){
var errorMsg = validataFunc();
if ( errorMsg ){
alert (errorMsg);
return false;
}
};
</script>
</body>
</html>
最后,如果去掉 strategies,我们还能认出这是一个策略模式的实现吗? 代码如下:
var S = function( salary ){
return salary * 4;
};
var A = function( salary ){
return salary * 3;
};
var B = function( salary ){
return salary * 2;
};
var calculateBonus = function( func, salary ){
return func( salary );
};
calculateBonus( S, 10000 ); // 输出:40000
JavaScript设计模式(策略模式)的更多相关文章
- javascript 设计模式-----策略模式
在<javascript设计模式>中,作者并没有向我们介绍策略模式,然而它却是一种在开发中十分常见的设计模式.最常见的就是当我们遇到一个复杂的表单验证的时候,常常需要编写一大段的if和el ...
- javascript设计模式——策略模式
前面的话 在程序设计中,常常遇到类似的情况,要实现某一个功能有多种方案可以选择.比如一个压缩文件的程序,既可以选择zip算法,也可以选择gzip算法.这些算法灵活多样,而且可以随意互相替换.这种解决方 ...
- javascript设计模式-策略模式
策略模式笔记 将定义的一组算法封装起来,使其相互之间可以替换. 封装的算法具有一定独立性,不会随客户端变化而变化. 与状态模式异同? 1. 结构上看,它与状态模式很像,也是在内部封 ...
- JavaScript设计模式 - 策略模式(表单验证)
表单提交的时候,总是要校验,不同的表单可能校验相同的功能. 为了避免代码重复的复制黏贴,使用策略模式,写出来的代码赏心悦目,且可扩展,还可以作为插件到处使用 <!DOCTYPE html> ...
- JavaScript实现策略模式
在开篇之前先分享今天看到的一句关于设计模式的话:将不变的部分和变化的部分隔开是每个设计模式的主题 请大家自行感受这句话的精髓所在,并且思考学习设计模式究竟能给我们编程带来什么样的东西,欢迎大家在文章下 ...
- JS设计模式——策略模式
设计模式高大上,业务代码用不上...平时用不上我们就可以忽略了吗? 非也,就像面试造火箭,工作拧螺丝一样.万一我们公司哪天要造火箭了,你得立马能上手. 同时,有些复杂的业务代码也可以用设计模式的思想去 ...
- 15. 星际争霸之php设计模式--策略模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
- [.net 面向对象程序设计深入](24)实战设计模式——策略模式(行为型)
[.net 面向对象程序设计深入](24)实战设计模式——策略模式(行为型) 1,策略模式定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它 ...
- linkin大话设计模式--策略模式
linkin大话设计模式--策略模式 Strategy [ˈstrætədʒi] 策略 策略模式用于封装系列的算法,这些算法通常被封装在一个称为Context的类中,客户端程序可以自由的选择任何一种 ...
- [.net 面向对象程序设计深入](26)实战设计模式——策略模式 Strategy (行为型)
[.net 面向对象程序设计深入](26)实战设计模式——策略模式 Strategy (行为型) 1,策略模式定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模 ...
随机推荐
- 带着问题看redux源码
前言 作为前端状态管理器,这个比较跨时代的工具库redux有很多实现和思想值得我们思考.在深入源码之前,我们可以相关注下一些常见问题,这样带着问题去看实现,也能更加清晰的了解. 常见问题 大概看了下主 ...
- RHEL 7.6系统安装配置图解教程
- 生成 XML 文档时出错。
找来找去,在CSDN 里看到高手解答了. 原来 WebService 传递参数时,不能使用 DataTable 如果需要使用DataTable 那就要用 DataSet 包装起来. 如果担心数据太 ...
- vscode 记住git用户密码
一.选中文件-->首选项-->设置,搜索git,在setting.json中添加 "git.path":"/bin/git.exe" 二.git b ...
- 手把手教你把5V的Arduino改造成3.3V版本
Arduino Pro Mini是基于ATmega328的微控制板,支持5V/3.3V电压.但是我们比较容易买到的是5V供电的版本,有的时候需要用到3.3V版本(比如我们的软控DAC).怎样才能让这款 ...
- quartz定时任务表达式案例
表示式 说明 "0 0 12 * * ? " 每天12点运行 "0 15 10 ? * *" 每天10:15运行 "0 15 10 * * ?&quo ...
- 部署k8s时容器中ping不通
192.168.42.120 | UNREACHABLE! => { "changed": false, "msg": "Fail ...
- Spring Boot启动的报错 Stopping service [Tomcat]
我遇到的问题是项目中使用java_websocket的WebSocketClient,由于性能要求,需要再Controller直接继承WebSocketClient, 在项目启动过程中调试进入spri ...
- 实时更新Excel文档外部数据源的数据
实时更新Excel文档外部数据源的数据 单元格区域.Excel 表.数据透视表或数据透视图均可以连接到外部数据源(数据源:用于连接数据库的一组存储的"源"信息.数据源包含数据库服务 ...
- Python排序之多属性排序
1.sort函数说明: sort函数是list类的一个方法,说明如下: sort(...)L.sort(cmp=None, key=None, reverse=False) -- stable sor ...