策略模式

策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换

注释: “并且使它们可以相互替换”,这句话在很大程度上是相对于静态类型语言而言的。因为静态类型语言中有类型检查机制,所以各个策略类需要实现同样的接口。当它们的真正类型被隐藏在接口后面时,它们才能被相互替换。而在 JavaScript这种“类型模糊”的语言中没有这种困扰,任何对象都可以被替换使用。因此,JavaScript中的“可以相互替换使用”表现为它们具有相同的目标和意图。

一个基于策略模式的程序至少由两部分组成:第一个部分是一组策略类,策略类封装了具体

的算法,并负责具体的计算过程;第二个部分是环境类 Context,Context接受客户的请求,随后

把请求委托给某一个策略类

现在假设有一个计算年终奖的需求,绩效为 S的人年终奖有 4倍工资,绩效为 A的人年终奖有 3倍工资,而绩效为 B的人年终奖是 2倍工资,用策略模式实现这个需求

// 策略类
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


strategies策略类负责算法部分,它们都有相同的目标,就是返回绩效值。calculateBonus负责把客户端请求委托给策略类,用户在调用calculateBonus时只需要传入参数即可

策略类并不一定要是算法计算,只要有相同的目标都可以封装成策略类,比如前端在进行表单验证时,会判断用户的输入值是否满足要求,这个需求也非常适合用策略类实现

<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>
// 策略对象
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.unshift(dom.value);
strategyAry.push(errorMsg);
// strategyAry => ['输入的值', 6, '用户名长度不能小于 6 位']
// strategyAry => ['输入的值', '用户名不能为空']
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: '用户名长度不能小于 6 位'
}]);
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;
}
};