定义
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
绩效奖金案例
策略模式说的简单一点,就是根据现有条件的不同,选择不同策略来完成目标功能。
例如我要知道一个直角三角形的面积,我可以根据两条直角边进行求解,也可以根据一条边和一个角求解,甚至更多解法,就看题目给出哪些已知条件,这就是策略模式。
又举个例子,我要去一个地方旅行,我可以坐火车,汽车,高铁,飞机,甚至可以骑自行车,这些我们都可以称之为策略,至于选择哪种策略,我们可以根据时间和金钱预算来选择不同的策略。
又比如,公司想要根据一个员工的绩效等级和工资来算年终奖,那么绩效等级和比例我们可以称之为策略,用代码来看看。
// 原始版本
function annualBonus (performance, salary) {
if (performance === 'A') {
return salary * 1.2; // 优秀则发放120%的奖金
} else if (performance === 'B') {
return salary; // 良好则全额发放
} else if (performance === 'C') {
return salary * 0.8; // 一般则发放80%
}
return 0; // 如果不及格则不发放奖金
}
console.log(annualBonus('A', 12000))
console.log(annualBonus('C', 8000))
console.log(annualBonus('D', 30000))
从上边的代码可以看到,该函数根据不同的绩效等级提供了不同的算法来实现该功能,同时,其弊端也显而易见。
- 代码体积非常大,且包含了多个
if-else - 如果新增一个绩效,需要修改
annualBonus的内容 我们接下来用策略模式对其进行重构
策略模式重构年终奖案例
策略模式包含包含至少两部分:策略组、环境
所谓的策略组,拿上面的例子来说就是根据不同的绩效等级提供的算法,而环境就是已有的已知条件,即员工的绩效等级和薪资。
// 定义策略组
var strategyGroup = {
'A': function (salary) {
return salary * 1.2;
},
'B': function (salary) {
return salary;
},
'C': function (salary) {
return salary * 0.8;
},
'D': function (salary) {
return 0;
}
}
// 定义环境
var annualBonus = function (level, salary) {
return strategyGroup[level](salary)
}
// 调用
console.log(annualBonus('A', 12000))
console.log(annualBonus('C', 8000))
console.log(annualBonus('D', 30000))
策略模式将具体实现方式从调用函数中抽离出来了,减少了if-else这样的多重调用,另外如果需要新增一种绩效,也不需要动环境中的代码,只需要在策略组中新增就行,增加了容错率,减少了环境的代码体积。
表单验证案例
背景:验证名称非空,手机号码格式合法,如果不合法则不予通过。
先模拟一个用户表单输入
var userName = '前端橘子君';
var phone = '14200000000';
// 原始代码
function check () {
if (!userName) {
return false;
}
if (!/^1(3|5|6|7|8|9)[0-9]{9}$/.test(phone)) {
return false;
}
return true;
}
弊端就不多说了,和年终奖案例是一样的,下边我们来对其用策略模式进行重构。
策略模式重构表单验证案例
// 定义策略组
var strategyGroup = {
userName: function () {
return !!userName;
},
phone: function () {
return !!/^1(3|5|6|7|8|9)[0-9]{9}$/.test(phone);
}
}
// 定义环境
function check (args) {
var checkList = args.map(item => strategyGroup[item]);
return checkList.every(ele => ele());
}
// 调用
var result = check(['userName', 'phone']);
console.log(result);
在该案例中,每一种校验规则都是一种策略,而校验字段就是环境,如果有需要新增校验,则直接在strategyGroup中新增即可,我们可以将这个封装为公共方法,这样没必要每个页面都写一遍验证规则了
上面的内容过于简单,不建议在项目中直接使用,可以改造一下。
// 定义策略组
var strategyGroup = {
userName: function (userName) {
return !!userName;
},
phone: function (phone) {
return !!/^1(3|5|6|7|8|9)[0-9]{9}$/.test(phone);
}
}
// 定义环境
function check (args) {
return args.map(item => strategyGroup[item.label](item.value)).every(ele => ele);
}
// 调用
var checkOptions = [
{
label: 'userName', // 需要校验的字段名
value: '前端橘子君' // 用户输入的值
},
{
label: 'phone', // 需要校验的字段名
value: '13000000000' // 用户输入的值
}
]
var result = check(checkOptions);
console.log(result);
优势
- 1、算法可以自由切换。
- 2、避免使用多重条件判断。
- 3、扩展性良好。
缺点
- 1、策略会逐渐增多
- 2、所有策略需要向外暴露
更多相关文档,请见:
线上地址 【前端橘子君】
GitHub仓库【前端橘子君】