**策略模式的定义是:**定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体 的算法,并负责具体的计算过程。 第二个部分是环境类 Context,Context接受客户的请求,随后 把请求委托给某一个策略类。要做到这点,说明 Context中要维持对某个策略对象的引用。
- 用策略模式设计计算年终奖的案例
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
使用策略模式的设计后,如果需要修改s评级的计算方法,可以使用strategies.s进行修改
策略模式的本质就是对同套流程的不同执行方法的选择,可以将不同的流程单独提出,写出策略类,可以在执行的时候,选择不同的策略;同时,策略内的方法可以方便的进行改写,不用深入代码内部进行删减修改。
多态在策略模式中的体现
通过使用策略模式重构代码,我们消除了原程序中大片的条件分支语句。所有跟计算奖金有 关的逻辑不再放在 Context中,而是分布在各个策略对象中。Context并没有计算奖金的能力,而 是把这个职责委托给了某个策略对象。每个策略对象负责的算法已被各自封装在对象内部。当我 们对这些策略对象发出“计算奖金”的请求时,它们会返回各自不同的计算结果,这正是对象多 态性的体现,也是“它们可以相互替换”的目的。替换 Context中当前保存的策略对象,便能执 行不同的算法来得到我们想要的结果。
用策略模式重构表单校验
<!DOCTYPE html>
<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;
}
}
};
/**
* 客户调用代码
**/
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; // 阻止表单提交
}
};
}
/**
* 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.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) { // 如果有确切的返回值,说明校验没有通过
return msg;
}
}
};
</script>
</body>
</html>
在修改某个校验规则的时候,只需要编写或者改写少量的代码。比如我们想将用户名输入框 的校验规则改成用户名不能少于 4个字符。可以看到,这时候的修改是毫不费力的。
validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空' );
// 改成:
validator.add( registerForm.userName, 'minLength:10', '用户名长度不能小于 10 位' );
- 在 JavaScript语言的策略模式中,策略类往往被函数所代替,这时策略模式就 成为一种“隐形”的模式。