话不多说,直接来看例子,比如我们需要计算员工工资,员工工资计算规则如下:
- 高级工:时薪为25块/小时
- 中级工:时薪为20块/小时
- 初级工:时薪为15块/小时
按每天10小时的工作时长来算。
一、第一版实现:
const calculateSalary = function (workerLevel, workHours = 10) {
if (workerLevel === 'high') {
return workHours * 25
}
if (workerLevel === 'middle') {
return workHours * 20
}
if (workerLevel === 'low') {
return workHours * 15
}
}
console.log(calculateSalary('high')) // 250
console.log(calculateSalary('middle')) // 200
这段代码具有明显的缺点:
- calculateSalary函数庞大,有许多的if else语句,这些语言需要覆盖所有的逻辑分支
- calculateSalary函数缺乏弹性,如果新增一种员工等级higher,需要修改calculateSalary函数的内部实现,违反
开放——封闭原则
。 - 算法的复用性差
二、第二版实现(函数组合):
当然,我们可以使用函数组合的方式重构代码,把每一个if中的逻辑单独抽离成一个函数。
const workerLevelHigh = function (workHours) {
return workHours * 25
}
const workerLevelMiddle = function (workHours) {
return workHours * 20
}
const workerLevelLow = function (workHours) {
return workHours * 15
}
const calculateSalary = function (workerLevel, workHours = 10) {
if (workerLevel === 'high') {
return workerLevelHigh(workHours)
}
if (workerLevel === 'middle') {
return workerLevelMiddle(workHours)
}
if (workerLevel === 'low') {
return workerLevelLow(workHours)
}
}
console.log(calculateSalary('high', 10)) // 250
console.log(calculateSalary('middle', 10)) // 200
这样会提高算法的复用性,但这种改善十分有限,calculateSalary函数
依旧庞大和缺乏弹性。
三、第三版实现(策略模式):
我们可以把不变的部分和变化的部分拆分开来。
- 不变的部分:算法的使用方式不变,都是根据某个算法取得计算后的工资数额;
- 变化的部分:算法的实现。
我们js的对象是key value
的形式,这可以帮助我们天然的替换掉if else
。
因此,我们可以定义对象的两部分:
- 针对变化的部分,我们可以定义一个策略对象,它封装了具体的算法,负责具体的计算过程
- 针对不变的部分,我们提供一个Context函数,它接受客户的请求,随后把请求委托给策略对象。
const strategies = {
"high": function (workHours) {
return workHours * 25
},
"middle": function (workHours) {
return workHours * 20
},
"low": function (workHours) {
return workHours * 15
},
}
const calculateSalary = function (workerLevel, workHours) {
return strategies[workerLevel](workHours)
}
console.log(calculateSalary('high', 10)) // 250
console.log(calculateSalary('middle', 10)) // 200