定义
定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。一个基于策略模式的程序至少有两部分组成:
- 策略类:封装具体的算法,并负责具体的计算过程
- 环境类 Context:
Context
接受客户请求,随后将请求委托给策略类
真实世界类比
假如你需要前往机场。 你可以选择乘坐公共汽车、 预约出租车或骑自行车。 这些就是你的出行策略。 你可以根据预算或时间等因素来选择其中一种策略。
模式实现-计算奖金
这里我们以年终奖的计算为例进行实现:
假设绩效为S
的人年终奖有 4 倍工资;A
的人年终奖有 3 倍工资;B
的人年终奖有 2 倍工资。
按照策略模式的基本组成我们进行分解:
// 第一步: 封装策略类
const strategies = {
S: salary => {
return salary * 4;
},
A: salary => {
return salary * 3;
},
B: salary => {
return salary * 2;
}
};
// 第二步:这里我们用calculateBonus函数充当Context来接收用户请求
const calculateBonus = (level, salary) => {
return strategies[level](salary);
};
console.log(calculateBonus("S", 20000)); // 输出:80000
console.log(calculateBonus("A", 10000)); // 输出:30000
更广义的“算法”
策略模式指的是定义一系列算法。但在实际的开发中,我们通常会把算法的含义扩散开来,使策略模式也可以用来封装一系列的业务规则。只要这些业务规则指向的目标一致,而且可以被替换使用。
模式实现-表单校验
在 web 应用中,涉及表单的部分几乎都离不开校验。
假设我们正在编写一个注册页面,在点击注册按钮前,需要校验以下几条逻辑:
- 用户名不能为空
- 密码长度不少于 6 位
- 手机号必须符合格式
// 第一步: 封装策略类
const strategies = {
isNonEmpty: (value, errorMsg) => {
if (value === "") {
return errorMsg;
}
},
minLength: (value, errorMsg) => {
if (value.length < 6) {
return errorMsg;
}
},
isMobile: (value, errorMsg) => {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
};
// 第二步:封装Validator类充当Context来接收用户请求并委托给strategy对象
class Validator {
constructor() {
this.cache = [];
}
add(dom, strategy, errorMsg) {
this.cache.push(() => {
const args = [dom.value, errorMsg];
return strategies[strategy].apply(dom, args);
});
}
start() {
this.cache.forEach(fn => {
const msg = fn();
if (msg) {
return msg;
}
});
}
}
// 使用
const registerForm = document.getElementById("registerForm");
function validataFunc() {
const validator = new Validator();
validator.add(registerForm.userName, "isNonEmpty", "用户名不能为空");
validator.add(registerForm.password, "minLength", "密码长度不能少于6位");
validator.add(registerForm.phone, "isMobile", "手机号码格式不正确");
const error = validator.start();
return error;
}
registerForm.onsubmit = () => {
const errorMsg = validataFunc();
if (errorMsg) {
alert(errorMsg);
return false;
}
};
可以看到,我们仅仅通过配置的方式就完成了一个表单的校验。
小结
在JavaScript语言的策略模式中,策略类往往被函数所代替,这时策略模式就成了一种“隐形”的模式。尽管如此,从根本上理解策略模式的理念不仅让我们对模式有这更透彻的了解,也可以让我们明白使用函数的好处。
参考
- 《深入设计模式》
- 《JavaScript 设计模式与开发实践》