策略模式
什么是策略模式
意图
策略模式(Strategy Pattern)定义了一组策略,分别在不同类中封装起来,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。
特点
- 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句
- 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法
缺点
- 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
- 策略模式造成很多的策略类。
实现策略模式
现在假如播放视频有四种用户:普通用户、VIP用户、VVIP用户和超前点播用户,每一个级别能比前一个级别提前看多三级。那么该如何实现呢?
仅完成需求的代码
首先大家肯定能想到这种编写方式
js
let canWatch = function(identity, num) {
if(identity == 'none') {
return num <= 3
}
if(identity == 'VIP') {
return num <= 6
}
if(identity == 'VVIP') {
return num <= 9
}
if(identity == 'advance ') {
return num <= 12
}
}
canWatch('VIP', 8) //false
这样写有三个缺点:
- canWatch 函数会过于大
- canWatch 违法开放-封闭原则,如果我想增加多以VVVIP或者修改VIP的特权就必须进入 canWatch函数内部修改
- 复用性差,其它地方不能有选择的复用这些特权判断逻辑。
使用组合函数
js
let identityNone = function(num) {
return num <=3
}
let identityVIP = function(num) {
return num <=6
}
let identityVVIP = function(num) {
return num <=9
}
let identityAdvance = function(num) {
return num <=12
}
let canWatch = function(identity, num) {
if(identity == 'none') {
return identityNone(num)
}
if(identity == 'VIP') {
return identityVIP(num)
}
if(identity == 'VVIP') {
return identityVVIP(num)
}
if(identity == 'advance ') {
return identityAdvance(num)
}
}
canWatch('VIP', 8) //false
使用组合函数来重构代码。将用户的各种特权判断提取到一个单独的函数。能清晰的看到每个级别的特权。但是依然有一个缺点,那就是 canWatch 过于庞大。
策略模式的实现
策略模式的目的就是将操作的使用和操作的实现分离开来。所以一般的策略模式会有两个部分组成:一个策略类,具体封装了操作的实现。第二个就是环境类Context,Context接受用户的请求,然后把请求委托给某一个策略类。
传统的面向对象方式实现
js
let identityNone = function() {}
identityNone.prototype.canWatch = function(num) {
return num <= 3
}
let identityVIP = function() {}
identityVIP.prototype.canWatch = function(num) {
return num <= 6
}
let identityVVIP = function() {}
identityVVIP.prototype.canWatch = function(num) {
return num <= 9
}
let identityAdvance = function() {}
identityAdvance.prototype.canWatch = function(num) {
return num <= 12
}
let Context = function() {
this.strategy = null
}
Context.prototype.setIdentity = function(strategy) {
this.strategy = strategy
}
Context.prototype.getPower = function(num) {
return this.strategy.canWatch(num)
}
let obj = new Context()
obj.setIdentity(new identityVIP())
console.log(obj.getPower(7)) // false
JavaSscript的实现
在 JavaSscript 中由于函数也是对象,所以更简单和直接的做法就是把 strategy 直接定义为函数:
js
let strategies = {
"None": function(num) {
return num <= 3
},
"VIP": function(num) {
return num <= 6
},
"VVIP": function(num) {
return num <= 9
},
"Advance": function(num) {
return num <= 12
}
}
同样 Context 也可以由函数来实现:
js
let context = function(identity, num) {
return strategies[identity](num)
}
context('VIP', 6) // true