定义
策略模式的目的就是将算法的使用与算法的实现分离开来。——《JavaScript设计模式与开发实践》
其由两部分组成:
1、多个策略类:封装了具体的算法
2、环境类:负责根据用户请求,调用特点的策略类,并将该策略类的执行结果返回给客户。
策略模式可以去除掉大量if-else语句,使得代码更易于切换、理解、扩展。
适用场景
1、需要多个if-else进行区分处理。
2、表单验证(扩展)。
实现示例
const strategies = {
'S': (salary) => {
return salary * 4;
},
'A': (salary) => {
return salary * 3;
},
'B': (salary) => {
return salary * 2;
},
'default': (salary) => {
return salary;
}
};
const calculateBonus = (level, salary) => {
const fn = strategies[level] || strategies['default'];
return fn.apply(strategies, salary);
};
console.log(calculateBonus('S', 20000)); // 输出:80000
console.log(calculateBonus('A', 10000)); // 输出:30000
项目中的运用
原始版本
function getMainView () {
const mainType = this.get('mainType'); //icfiles netdisk
if (mainType == 'icfiles') {
return MainIcfiles
} else if (mainType == 'netdisk') {
return MainNetdisk;
} else if (mainType == 'card') {
return MainCard;
} else if(mainType == 'crm') {
return MainCRM;
} else if(mainType == 'email') {
return MainEmail;
} else {
return MainImage;
}
};
很显然getMainView方法中充斥着if-else,可以利用策略模式进行重构。
改进版本
function getMainView () {
const strategies = {
'icfiles': MainIcfiles,
'netdisk': MainNetdisk,
'card': MainCard,
'crm': MainCRM,
'email': MainEmail,
'default': MainImage
};
getMainView = function () {
const mainType = this.get('mainType'); //icfiles netdisk
return strategies[mainType] || strategies['default'];
}
getMainView();
};
利用策略模式重构后的代码看上去清爽很多,而且不会因为后续各种判断条件的膨胀而影响性能。
扩展运用
表单验证
策略模式不仅仅只是对算法的“封装”,也可以用来封装“业务规则”,《JavaScript设计模式与开发实践》书中的例子就是“策略模式”在表单验证中的运用。
以下代码是我根据书中示例代码,加上自己的理解进行重写得到的。
/***********************策略对象**************************/
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;
}
}
};
/***********************Validator 类**************************/
// 用与管理各表单字段的校验策略
class Validator {
constructor() {
this.cache = [];
}
add (dom, rules) {
const self = this;
if (!Array.isArray(rules)) {
rules = [rules];
}
rules.forEach(rule => {
// strategy是一个可以设置参数的校验规则如 minLength:5 表示最小长度为5
const strategyAry = rule.strategy.split(':');
const errorMsg = rule.errorMsg;
self.cache.push(() => {
// 需要运用的目标策略
const strategy = strategyAry.shift();
strategyAry.unshift(dom.value);
strategyAry.push(errorMsg);
return strategies[strategy].apply(dom, strategyAry);
});
});
}
start () {
for (let i = 0, validatorFunc; validatorFunc = this.cache[i++];){
var errorMsg = validatorFunc();
if (errorMsg) {
return errorMsg;
}
}
}
};
/***********************客户调用代码**************************/
const registerForm = document.getElementById( 'registerForm' );
const validataFunc = function() {
const validator = new Validator();
validator.add( registerForm.userName, [
{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空'
},
{
strategy: 'minLength:6',
errorMsg: '用户名长度不能小于10 位'
}
]);
validator.add( registerForm.password, {
strategy: 'minLength:6',
errorMsg: '密码长度不能小于6 位'
});
const errorMsg = validator.start();
return errorMsg;
}