javascript设计模式 - 策略模式
mhahaha opened this issue · 0 comments
mhahaha commented
俗话说,条条大路通罗马,完成一件事情,我们往往有很多选择,根据自身不同的条件我们可以有不同的选择方案。比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路。
- 如果没有时间但是不在乎钱,可以选择坐飞机。
- 如果没有钱,可以选择坐大巴或者火车。
- 如果再穷一点,可以选择骑自行车。
这类选择过程也即是我们即将学习的策略模式。
策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
示例:编写一个计算奖金(bonus)的算法,薪资(salary) 为最小单元
- 级别(level)S bonus = salary * 3
- 级别(level)A bonus = salary * 2
- 级别(level)B bonus = salary * 1
可以看出,计算出员工奖金,我们需要知道员工的级别及薪资,我们可以编写一个方法,将级别和薪资当做参数传进去,这样我们就可以得到员工奖金。如下:
const calculateBonus = (performanceLevel, salary) => {
if (performanceLevel === 'S') {
return salary * 3
}
if (performanceLevel === 'A') {
return salary * 2
}
if (performanceLevel === 'B') {
return salary * 1
}
}
calculateBonus('S', 8000)可以发现,这段代码十分简单,但是存在着显而易见的缺点。
- calculateBonus 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有的逻辑
分支。 - calculateBonus 函数缺乏弹性,如果增加了一种新的绩效等级 C,或者想把绩效 S 的奖金
系数改为 5,那我们必须深入 calculateBonus 函数的内部实现,这是违反开放-封闭原则的。 - 算法的复用性差,如果在程序的其他地方需要重用这些计算奖金的算法呢?我们的选择
只有复制和粘贴。
在javascript中,声明一个对象的成本是很低的,我们根据策略模式的定义来改造下,上面计算函数我们完全可以简化成一个对象来进行维护,key是级别,value则是一个计算奖金的函数,代码如下:
const strategies = {
'S': salary => salary * 3,
'A': salary => salary * 2,
'B': salary => salary * 1
};
const calculateBonus = (level, salary) => strategies[ level ]( salary )
calculateBonus( 'S', 20000 )这样一来,我们消除了大片的if-esle条件分支语句,使实现过程更加简洁,只需维护好strategies这个策略对象即可。
以上示例我们可以了解到策略模式的基本使用,那在我们实际开发过程,其实很多地方都可以用策略模式来进行改进,比如我们前端的一些校验,比较急躁的时候可能会出现上面庞大的if-else来进行各类验证条件的编写,那现在我们用策略模式来改进试试看,实现代码如下:
// 策略对象
const strategies = {
isNonEmpty: (value, errorMsg) => {
if (value === '') {
return errorMsg
}
},
minLength: (value, length, errorMsg) => {
if (value.length < length) {
return errorMsg
}
},
isMobile: (value, errorMsg) => {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg
}
}
}
// 处理校验的校验类
class Validator {
constructor() {
this.cache = []
}
add (ele, rules) {
for (let i = 0, rule; (rule = rules[i++]);) {
const strategyAry = rule.strategy.split(':')
const errorMsg = rule.errorMsg
this.cache.push(() => {
const strategy = strategyAry.shift()
strategyAry.unshift(ele.value)
strategyAry.push(errorMsg)
return strategies[strategy].apply(ele, strategyAry)
})
}
}
start () {
for (let i = 0, validatorFunc; (validatorFunc = this.cache[i++]);) {
const errorMsg = validatorFunc()
if (errorMsg) {
return errorMsg
}
}
}
}
// demo
const registerForm = {
userName: {
value: 'lalalalala'
},
password: {
value: 'mimamima'
},
phoneNumber: {
value: 13123456789
}
}
const validataFunc = () => {
const validator = new Validator()
validator.add(registerForm.userName, [
{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空'
},
{
strategy: 'minLength:6',
errorMsg: '用户名长度不能小于 6 位'
}
]);
validator.add(registerForm.password, [
{
strategy: 'minLength:6',
errorMsg: '密码长度不能小于 6 位'
}
]);
validator.add(registerForm.phoneNumber, [
{
strategy: 'isMobile',
errorMsg: '手机号码格式不正确'
}
])
const errorMsg = validator.start() // 开始校验,并取得校验后的返回信息
return errorMsg
}
const errorMsg = validataFunc()
if (errorMsg) {
console.log(errorMsg)
}这样我们按照策略模式新增并维护一个策略类,然后按照规则添加各数据项的校验就好,保证了功能代码可读性及扩展性。
总结
实际开发过程,当发现一个方法逐渐变得庞大,if-else写得让你难受的时候,试试策略模式改造能否解决痛点。