将不变的部分和变化的部分隔开是每个设计模式的主题
什么是策略模式?
策略模式是将不同算法进行合理的分类与单独封装,从而让不同算法之间可以互相替换而不会影响到算法的使用者。
接下来我们用一个简单的例子来说明下策略模式:
eg: 根据传入的分数来判断当前分数的状态是不及格,及格了,还是优秀。
// 一般写法
const score1 = 59;
function judgeScore(score) {
if(score < 60 ) {
return '不及格'
}else if(score > 60 && score < 85) {
return '及格了'
}else if(score >= 85) {
return '优秀'
}
}
judgeScore(58); // 不及格
judgeScore(66); // 及格
judgeScore(88); // 优秀
如上所示:
我们需要通过多个if…else…对传进来的分数进行判断,如果分的更细的话, 那代码就会显的非常冗余,不好维护。
接下来我们看看用策略模式实现的效果:
// 策略模式
let strategies = {
noPass: function(score, tipText) {
if(score < 60) {
return tipText;
}
},
pass: function(score, tipText) {
if(score > 60 && score < 85) {
return tipText
}
},
excellent: function(score, tipText) {
if(score >= 85) {
return tipText;
}
}
}
function judgeScore(score) {
let noPass = strategies['noPass'](score, '不及格');
let pass = strategies['pass'](score, '及格');
let excellent = strategies['excellent'](score, '优秀');
let res = noPass || excellent || pass;
return res;
}
judgeScore(58); // 不及格
judgeScore(66); // 及格
judgeScore(88); // 优秀
策略模式最经典的是应用在表单验证上:
// 一般写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="xxxx" id="myForm">
<input type="text" placeholder="userName" name="userName" />
<input type="text" placeholder="password" name="password"/>
<button>登录</button>
</form>
<script>
const myForm = document.getElementById('myForm');
myForm.onsubmit = function () {
if(myForm.telephone.value === '') {
console.log('电话号码不能为空');
return false;
}else if(myForm.userName.password === '') {
console.log('密码不能为空');
return false;
}
}
</script>
</body>
</html>
如上述代码:
同样代码很好理解, 但是每增加一个表单项,我们就需要增加一个if语句。
接下里我们应用策略模式来改造一下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="xxxx" id="myForm">
<input type="text" placeholder="telephone" name="telephone" />
<input type="text" placeholder="至少8位数" name="password"/>
<button>登录</button>
</form>
<script>
const myForm = document.getElementById('myForm');
let strategies = {
isNotEmpty: function(value, tipText) {
if(vlaue === '') {
return tipText;
}
},
minLength: function(value, length, tipText) {
debugger
if(value.length < length) {
return tipText;
}
},
matchTelephone: function(value, tipText) {
if(!/^1[3|4|5|7|8|9][0-9]{9}$/.test(value)) {
return tipText;
}
}
}
class Validator {
constructor(strategies) {
this.strategies = strategies;
this.cache = [];
}
add(dom, rule, tipText) { // add(dom, 'minLength:8', '***')
this.cache.push(function() {
const r = rule.split(':');
const strategy = r.shift();
const params = [...r, tipText];
return strategies[strategy](dom, ...params);
})
}
startValidate() {
for(let i = 0; i < this.cache.length; i++) {
let msg = this.cache[i]();
if(msg) {
return msg;
}
}
}
}
let validateFunc = function() {
let validator = new Validator();
validator.add(myForm.telephone.value, 'matchTelephone', '请输入正确的手机号格式');
validator.add(myForm.password.value, 'minLength:8', '密码至少8位数');
let tipText = validator.startValidate();
return tipText;
}
myForm.onsubmit = function(e) {
const tipText = validateFunc();
if(tipText) {
console.log(tipText);
return false // 有错误,阻止表单提交
}
}
</script>
</body>
</html>