策略模式——定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体 的算法,并负责具体的计算过程。第二个部分是环境类 Context,Context 接受客户的请求,随后 把请求委托给某一个策略类。要做到这点,说明 Context 中要维持对某个策略对象的引用。
面向对象
// 策略类——封装具体算法
var performanceS = function () {}
performanceS.prototype.calculate = function (salary) {
return salary * 4
}
var performanceA = function () {}
performanceA.prototype.calculate = function (salary) {
return salary * 3
}
var performanceB = function () {}
performanceB.prototype.calculate = function (salary) {
return salary * 2
}
// 环境类——接受客户的请求,然后把请求委托给某个策略类
var Bonus = function () {
this.salary = null // 原始工资
this.strategy = null // 绩效等级对应的策略对象
}
Bonus.prototype.setSalary = function (salary) {
this.salary = salary // 设置员工的原始工资
}
Bonus.prototype.setStrategy = function (strategy) {
this.strategy = strategy // 设置员工绩效等级对应的策略对象
}
Bonus.prototype.getBonus = function () {
// 取得奖金数额
return this.strategy.calculate(this.salary) // 把计算奖金的操作委托给对应的策略对象
}
// 使用
var bonus = new Bonus()
bonus.setSalary(10000)
bonus.setStrategy(new performanceS()) // 设置策略对象
console.log(bonus.getBonus()) // 输出:40000
bonus.setStrategy(new performanceA()) // 设置策略对象
console.log(bonus.getBonus()) // 输出:30000
基于JavaScript
在 JavaScript 语言中,函数也是对象,所以更简单和直接的做法是把 strategy 直接定义为函数
var strategies = {
S: function (salary) {
return salary * 4
},
A: function (salary) {
return salary * 3
},
B: function (salary) {
return salary * 2
}
}
//同样,Context 也没有必要必须用 Bonus 类来表示,我们依然用 calculateBonus 函数充当 Context 来接受用户的请求。经过改造,代码的结构变得更加简洁:
var calculateBonus = function( level, salary ){
return strategies[ level ]( salary )
}
策略模式的优缺点
优点
- 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
- 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它 们易于切换,易于理解,易于扩展。
- 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
- 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻 便的替代方案。 缺点
- 首先,使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的 逻辑堆砌在 Context 中要好。
- 其次,要使用策略模式,必须了解所有的 strategy,必须了解各个 strategy 之间的不同点, 这样才能选择一个合适的 strategy。比如,我们要选择一种合适的旅游出行路线,必须先了解选 择飞机、火车、自行车等方案的细节。此时 strategy 要向客户暴露它的所有实现,这是违反最少 知识原则的。