1、模式定义
策略模式,把定义的一组算法封装起来,使其相互之间可以替换。封装的算法具有一定的独立性,不会随客户端变化而变化。
2、策略模式 vs. 状态模式
从结构上看,策略模式和状态模式很像,也是在内部封装一个对象,然后通过返回的接口对象来实现对内部对象的调用。不同的是,策略模式不需要管理状态,状态之间也没有依赖关系,策略之间可以相互替换,策略对象内部保存的是相互独立的算法。
策略模式,就像一个活诸葛,对同一件事情的处理,总有多种可能的计谋,每次都可以随心所欲地选择一种计谋来达到不同种的结果。
3、策略模式 举例:设计商品促销策略
// 策略模式:价格策略对象
var PriceStrategy = function() {
// 内部算法对象
var strategy = {
// 策略1:100返30
return30: function(price) {
// +price 把字符串转化为数字类型
return +price + parseInt(price/100)*30;
},
// 策略2:100返50
return50: function(price) {
return +price + parseInt(price/100)*50;
},
// 策略3:打9折
percent90: function(price) {
// js在执行小数除法运算时有bug,因此先转化为整数再执行除法运算
return price*100*90/10000;
},
// 策略3:打8折
percent80: function(price) {
return price*100*80/10000;
},
// 策略3:打5折
percent50: function(price) {
return price*100*50/10000;
}
}
// 策略算法的调用接口
return function(algorithm, price) {
return stragtegy[algorithm] && stragtegy[algorithm](price);
}
}();
// 小测一下
var yourPrice = PriceStrategy('return50', '314.67');
console.log(yourPrice);
通过测试可发现,使用策略模式,我们无须关心算法的具体实现过程。我们能简简单单地通过策略对象所提供的接口方法来直接调用其内部封装的指定策略。
4、科普一下
事实上,jQuery动画的缓冲函数就是运用策略模式来实现的,当我们在使用jQuery的animate动画时,传入不同的运动算法,就可以实现不同的动画运动曲线。如下代码中的 “linear”/”swing”就是该策略模式中的两种策略:
$('div').animate( {width:'200px'}, 1000, 'linear');
$('div').animate( {width:'200px'}, 1000, 'swing');
另一个动画插件easing.js,也是使用策略模式实现了一个jQuery缓冲动画算法插件,它的缓冲函数中有30种算法策略。如下代码中的 “easeOutQuart”/”easeOutBounce”就是其中的两种策略:
$('div').animate( {width:'200px'}, 1000, 'easeOutQuart');
$('div').animate( {width:'200px'}, 1000, 'easeOutBounce');
5、策略模式 再举例:用于表单验证
除此之外,还有很多地方用到了策略模式。比如在表单验证模块中,每一种验证表单的方式都可以被看成是一种策略,本质上每种验证表单的策略就是一组正则算法。
// 策略模式:表单正则验证策略对象
var InputStrategy = function() {
var strategy = {
// 验证策略1:是否为空
notNull: function(value) {
return /\s+/.test(value) ? '表单不能为空,请输入内容!' : '';
},
// 验证策略2:是否是一个数字
isNumber: function(value) {
return /^[0-9]+(\.[0-9]+)?$/.test(value) ? '' : '请输入数字!';
},
// 验证策略3:是否是本地座机
isPhone: function(value) {
return /^\d{3}\-\d{8}$|^\d{4}\-\d{7}$/.test(value) ? '' : '座机号有误,请检查后重新输入!';
}
}
// 对外接口
return {
// 验证表单
check: function(type, value) {
// 去除value的首尾空字符
value = value.replace(/^\s+|\s+$/g, '');
return strategy[type] ? strategy[type](value) : '没有该类型的检测方法!';
},
// 添加策略:向策略模式中添加新的验证策略
addStrategy: function(type, fn) {
strategy[type] = fn;
}
}
}
和上述促销策略模式不同的是,这个表单验证的策略模式,我们给了一个 addStrategy方法,方便用户扩展新的验证策略。测试如下:
// 向InputStragegy中添加一条验证“昵称”的策略
InputStrategy.addStrategy('isNickname', function(value) {
return /^[a-zA-Z]\w{3,7}$/.test(value) ? '' : '请输入4-8位的昵称,如:YYQH';
});
使用这个表单验证的策略模式来验证表单,测试代码如下:
// 外观模式:简化元素的获取
function $tag(tag, context) {
context = context || document;
return context.getElementByTagName(tag)[0];
}
// 验证表单,提交表单
$tag('button').onclick = function() {
// 获取表单内容
var value = $tag('input').value;
// 在页面上显示表单的验证结果
$tag('span').innerHTML = InputStrategy.check('isNickname', value);
}
6、小结
策略模式使得算法脱离于模块逻辑而独立存在,我们便可以专心研发算法,而不必受到模块逻辑的约束。
策略模式最主要的特色就是创建了一系列策略算法,每组算法处理的业务都是相同的,只是处理过程或者处理结果不一样,所以它们又是可以相互替换的。这解决了算法和使用者之间的耦合。在测试层面上,由于每组算法是相互独立的,该模式更方便我们对每组算法执行单元测试,以保证算法的质量。
END 2019-03-01