策略模式
策略模式:将定义的一组算法封装起来,使其互相之间可以替换。封装的算法具有一定独立性,不会随着客户端变化而变化。
促销活动
那么我们理解了设计模式中的策略模式有什么应用场景呢?比如我们做一些电商类的项目,比如每一年的双十一都会搞一些促销活动,一些商品8折、一些9折出售,然后区分用户等级:普通用户购买金额满100返30,钻石用户满100返50。可能一开始我们看到这样的需求就会写出这样的代码:
// 8折
function parcent80(price){...}
// 9折
function parcent80(price){...}
// 满100返30
function return30(price){...}
// 满100返50
function return50(price){...}
这样写是不好的,我们应该内部封装一个对象,然后通过返回的接口对象供外部实现对内部对象的调用,策略模式是不需要管理状态、状态间没有依赖关系、策略之间可以互相替换、在策略对象内部保存的是互相独立的一些算法。
策略对象
为实现对每种促销方式,我们首先需要将这些算法封装在一个策略对象内,然后对每一种促销方式的策略调用时,直接对策略对象中的算法调用即可,而策略算法又独立地封装在策略对象内。为了方便我们的管理与使用,我们需要返回一个调用接口对象来实现对策略算法的调用。
// 促销方式的策略对象
const PriceStrategy = function(){
// 内部算法对象
const stragtegy = {
// 8折
parcent80(price){
return price * 100 * 80 / 10000
},
// 9折
parcent90(price){
return price * 100 * 90 / 10000
},
// 满100返30
return30(price){
// 假如price是100 那么就返回130
return +price + parseInt(price / 100) * 30
},
// 满100返50
return50(price){
// 同理
return +price + parseInt(price / 100) * 50
}
}
// 返回策略算法调用接口
return function(mode,price){
return stragtegy[mode] && stragtegy[mode](price)
}
}()
// 假如要求一个价格为300的商品的钻石用户的优惠价格
const price = PriceStrategy('parcent80',300)
// price ----> 240
策略模式使我们在外部看不到算法的具体实现,我们只需要通过策略对象返回的接口方法直接调用内部封装的某种策略算法就可以了。
思考哪些地方使用到了这种模式?
在vue、react等这些数据驱动的框架出现之前,我们开发者都是在使用DOM驱动的框架,比如大名鼎鼎的jQuery,相信大家都对以下代码都不陌生。
$('div').animate({width:'200px',1000,'linear'})
$('div').animate({width:'200px',1000,'swing'})
没错的!jQuery中的缓冲函数/动画函数就是用策略模式来实现的,比如它内部给我们提供的linear、swing的两种曲线就是两种策略算法。
扩展-表单验证
当然,除了上面这种促销使用到策略模式,还有很多地方都可以用上策略模式来简化我们实现业务需求。比如很常见的表单验证需求。
// 表单正则验证策略对象
const InputStrategy = function (){
const strategy = {
// 判断是否为空
notNull(value){
return /\s+/.test(value) ? '请输入内容!' : `内容是:${value}`
},
// 判断是否是一个数字
checkNumber(value){
return /^[0-9]+(.[0-9]+)?$/.test(value) ? `数字是:${value}` : '请输入数字!'
}
}
// 对外暴露接口
return {
check:function(type,value){
// 去除首尾空白符
value = value.replace(/^\s+|\s+$/g,'')
return strategy[type] ? strategy[type](value) : '没有该类型的检测方法'
}
}
}()
// 检验输入的是否为空
const res = InputStrategy.check('notNull','标题')
console.log(res) // 内容是:标题
const res1 = InputStrategy.check('checkNumber','1111')
console.log(res1) // 数字是:1111
总结
策略模式最主要的特色是创建一系列策略算法,每组算法处理的业务都是相同的,只是处理的过程或者处理的结果不一样,所以它们又是可以相互替换的,这样就解决了算法与使用者之间的耦合。在测试层面上讲,由于每组算法之间的独立性,该模式更方便对每组算法进行单元测试,保证算法的质量。
策略模式的优点:
- 策略模式封装了一组代码簇,并且封装了代码互相之间独立,便于对算法的重复引用,提高了算法的复用率。
- 策略模式与继承相比在类的继承中继承的方法是被封装在类中,因此当需求很多算法时,就不得不创建出多种类,这样会导致算法与算法的使用者耦合在一起,不利于算法的独立演化,并且在类的外部改变类的算法难度也是极大的。
- 同状态模式(也是设计模式中的一种)一样,策略模式也是一种优化分支判断语句的模式,采用策略模式对算法封装使得算法更利于维护。
JavaScript设计模式-策略模式