策略模式(Strategy)
策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
解析:就是定义一系列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。在客户对 Context 发起请求的时候,Context 总是把请求委托给这些策略对象中间的某一个进行计算。
使用场景:
- 校验器
- 缓动动画
- 可替换的计算
将不变的部分和变化的部分分隔开是每个设计模式的主题。
策略模式的目的就是将算法的使用与算法的实现分离开来。
比如:
在我们不使用设计模式的情况下,总会这么写:
javascript
const calculateBonus = function(performanceLevel, salary){
if(performanceLevel === 'S') {
return salary * 4;
}
if(performanceLevel === 'A') {
return salary * 3;
}
if(performanceLevel === 'B') {
return salary * 2;
}
}
calculateBonus('B', 20000);
calculateBonus('S', 6000);
缺点:
- 包含太多
if-else
语句。 - 缺乏弹性,如果需要增加多个等级的时候,需要深入函数内部实现。
- 算法复用性差。
使用策略模式下:
javascript
const strategies = {
S: function(salary) {
return salary * 4;
},
A: function(salary) {
return salary * 3;
},
B: function(salary) {
return salary * 2;
}
}
const calculateBonus = function(level, salary){
return strategies[level](salary);
}
calculateBonus('S', 20000);
若要增加其他等级,直接修改 strategies
即可,调用完全不用改。
例如:使用策略模式实现一个表单检验器validator
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/xxx" id="resForm" method="post">
姓名:<input type="text" name="username" placeholder="请输入你的姓名">
密码:<input type="password" name="pwd" placeholder="请输入你的密码">
手机号码:<input type="text" name="phone" placeholder="请输入你的手机号码">
<button>提交</button>
</form>
<script>
const resForm = document.getElementById('resForm')
resForm.addEventListener('submit', function () {
const errMsg = validatFunc()
alert(errMsg)
if (errMsg) {
return false
}
})
const validationRules = {
isNonEmpty: function (value, errMsg) {
if (value === '') {
return errMsg
}
},
minLength: function (value, length, errMsg) {
if (value.length < length) {
return errMsg
}
},
isPhone: function (value, errMsg) {
const phoneRE = /^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/;
if (phoneRE.test(value)) {
return errMsg
}
}
}
class Validator {
constructor() {
this.cache = [];
}
add(dom, rules) {
rules.forEach(rule => {
let arr = rule.type.split(':');
const errMsg = rule.errMsg;
this.cache.push(() => {
const type = arr.shift();
arr.unshift(dom.value);
arr.push(errMsg);
return validationRules[type].apply(dom, arr);
})
})
}
start() {
for (let i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
const errorMsg = validatorFunc();
if (errorMsg) {
return errorMsg;
}
}
}
}
const validatFunc = function () {
const validator = new Validator();
validator.add(resForm.username, [{
type: 'isNonEmpty',
errMsg: '姓名不能为空'
}, {
type: 'minLength:2',
errMsg: '姓名不能少于2位'
}])
validator.add(resForm.pwd, [{
type: 'isNonEmpty',
errMsg: '密码不能为空'
}, {
type: 'minLength:2',
errMsg: '密码不能少于6位'
}])
validator.add(resForm.phone, [{
type: 'isPhone',
errMsg: '请输入正确的手机号码'
}])
const errMsg = validator.start();
return errMsg
}
</script>
</body>
</html>