策略模式指的是定义一系 列的算法,把它们一个个封装起来。将不变的部分和变化的部分隔开是每一个设计模式的主题,策 略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来。html
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体 的算法,并负责具体的计算过程。 第二个部分是环境类 Context,Context接受客户的请求,随后 把请求委托给某一个策略类。要作到这点,说明 Context中要维持对某个策略对象的引用算法
<!--algorithm-->
var strategies = {
s: function(salary){
return salary *4;
},
a: function(salary){
return salary * 3;
},
b: function(salary){
return salary * 2
}
}
<!--context-->
var calculateBonus = function(level, salary){
return strategies[level](salary)
}
复制代码
在实际开发中,咱们一般会把算法的含义扩散开来,使策略模式也能够用来封装 一系列的“业务规则”。只要这些业务规则指向的目标一致,而且能够被替换使用,咱们就能够 用策略模式来封装它们。设计模式
在一个 Web项目中,注册、登陆、修改用户信息等功能的实现都离不开提交表单。 在将用户输入的数据交给后台以前,经常要作一些客户端力所能及的校验工做,好比注册的 时候须要校验是否填写了用户名,密码的长度是否符合规定,等等。这样能够避免由于提交不合 法数据而带来的没必要要网络开销。网络
假设咱们正在编写一个注册的页面,在点击注册按钮以前,有以下几条校验逻辑。app
<body>
<form action="http:// xxx.com/register" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/ >
请输入密码:<input type="text" name="password"/ >
</body>
复制代码
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;
}
}
复制代码
下面咱们将用策略模式来重构表单校验的代码,很显然第一步咱们要把这些校验逻辑都封装 成策略对象:dom
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, rule, errorMsg){
var ary = rule.split(':');// 把 strategy 和参数分开
this.cache.push(function(){// 把校验的步骤用空函数包装起来,而且放入 cache
var strategy = ary.shifg();// 用户挑选的 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){ // 若是有确切的返回值,说明校验没有经过
return msg;
}
}
}
复制代码
使用策略模式重构代码以后,咱们仅仅经过“配置”的方式就能够完成一个表单的校验, 这些校验规则也能够复用在程序的任何地方,还能做为插件的形式,方便地被移植到其余项 目中。post
在修改某个校验规则的时候,只须要编写或者改写少许的代码。好比咱们想将用户名输入框 的校验规则改为用户名不能少于 4个字符。能够看到,这时候的修改是绝不费力的。代码以下:ui
validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空' );
// 改为:
validator.add( registerForm.userName, 'minLength:10', '用户名长度不能小于 10 位' );
复制代码
<!--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();
})
})(rule)
}
}
Validator.prototype.start = function(){
for(var i=0, validatorFunc;validatorFunc = this.cache[i++];){
var errorMsg = validatorFunc();
if(errorMsg){
return errorMsg;
}
}
}
复制代码
优势:this
缺点: