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,策略模式定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模 ...
随机推荐
- cookies ,session,localstorage讲解
一 .cookie (1)简介 因为HTTP协议是无状态的,服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现.在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两饮料.最 ...
- 【软件工程】Alpha冲刺 (6/6)
链接部分 队名:女生都队 组长博客: 博客链接 作业博客:博客链接 小组内容 恩泽(组长) 过去两天完成了哪些任务 描述 tomcat的学习与实现 服务器后端部署,API接口的beta版实现 后端代码 ...
- 1.分布式配置中心 spring-cloud-config
pring Cloud 版本:2.1.0.RELEASE 一.server端 1.maven依赖 <dependency> <groupId>org.springframewo ...
- 如何使用git send-email发送补丁?
答: git send-email <patch-name> --to <username>@<example>.com --cc <username> ...
- vue项目报错
在项目根目录下的.eslintrc.js中的rules下添加以下内容: /*代表不用eslint检测代码规范*/ "useEslint":false, /* tab和空格混用缩进, ...
- es6 单例
class Singleton { constructor() { this.conn = this.connect(); } static getInstance() { if (!Singleto ...
- 【JVM学习笔记】线程上下文类加载器
有许多地方能够看到线程上下文类加载的设置,比如在sun.misc.Launcher类的构造方法中,能够看到如下代码 先写一个例子建立感性认识 public class Test { public st ...
- 前端面试经典题之apply与call的比较
在讲apply和call之前,我们需要先清楚在js中,this指向的是什么. 大家可以参考一下阮一峰老师写的关于JavaScript中this的原理讲解文章:http://www.ruanyifeng ...
- hydra 使用
Hydra介绍 Hydra是一个并行登录破解器,支持多种攻击协议.它非常快速和灵活,新模块易于添加.该工具使研究人员和安全顾问能够展示远程获得对系统未经授权的访问是多么容易. 它支持:Cisco AA ...
- IE11报错:[vuex] vuex requires a Promise polyfill in this browser的问题解决
昨天其他同事反馈IE浏览器无法打开线上的应用,查看了一下,发现控制台报以下错误: 发现和vuex有关系,去其官网查看了一下文档,发现关于IE浏览器的Promise的问题: 在vue-cli(webpa ...