深色模式
策略模式
背景
假设我们有一个表单,如下所示,需要在表单提交之前验证表单各项数据, 我们该如何处理呢?
html
<form action="http://liuzunkun.com" method="post" id="registerform">
<label for="name">账户 </label>
<input id="name" type="text" />
<label for="password">口令 </label>
<input id="password" type="password" />
<label for="tel">手机号 </label>
<input id="tel" type="tel" />
<button type="submit">注册系统</button>
<div id="errmsg"></div>
</form>
一个常用的处理代码书写方式是
js
const registerform = document.getElementById('registerform');
registerform.onsubmit = function (event) {
event.preventDefault();
if (!registerform.name.value || registerform.name.value.length < 6) {
alert('请输入正确账户名');
return;
}
if (!registerform.password.value || registerform.name.value.length < 6) {
alert('请输入正确账户密码');
return;
}
const mobileReg = /^(13[0-9]|14[5-9]|15[0-3,5-9]|16[6]|17[0-8]|18[0-9]|19[8,9])\d{8}$/;
if (!mobileReg.test(egisterform.tel.value)) {
alert('请输入正确手机号');
return;
}
// 之后的逻辑
};
上面这种写法能满足需求。缺点是有很多判断逻辑,代码不够简洁。策略模式能够很好满足需求。
原理
策略模式将代码执行分为两个部分:
- 将具体执行算法拆分为各个策略类执行
- 环境类 context 接收客户请求,并将请求委托给合适的策略类执行
比如上面的需求中
- 定义一个 Validator Context 来接收请求
- 定个各种策略类执行的方案
代码说明
- 定义不同的策略执行算法
js
const strategies = {
isNotEmpty: function (value, errMsg) {
if (!value) return errMsg;
},
minLength: function (value, length, errMsg) {
if (!value || value.length < length) return errMsg;
},
isMobile: function (value, errMsg) {
const mobileReg = /^(13[0-9]|14[5-9]|15[0-3,5-9]|16[6]|17[0-8]|18[0-9]|19[8,9])\d{8}$/;
if (!mobileReg.test(value)) return errMsg;
},
};
- Context 接收客户请求
这里我们定义了一个 Validator 对象类来接收请求
js
// 验证类接收表单项的验证请求
Validator.prototype.add = function (elem, rules = []) {};
// 验证类开始执行各种策略算法
Validator.prototype.start = function () {};
- 根据验证算法请求,对表单发起不同的执行策略
js
function validateFn() {
const validator = new Validator();
validator.add(registerform.name, [
{
strategy: 'isNotEmpty',
errMsg: '账户不能为空',
},
{
strategy: 'minLength:6',
errMsg: '账户名长度不能<6',
},
]);
validator.add(registerform.password, [
{
strategy: 'isNotEmpty',
errMsg: '口令不能为空',
},
{
strategy: 'minLength:6',
errMsg: '口令名长度不能<6',
},
]);
validator.add(registerform.tel, [
{
strategy: 'isNotEmpty',
errMsg: '手机号不能为空',
},
{
strategy: 'isMobile',
errMsg: '请输入正确的手机号',
},
]);
return validator.start();
}
- 接收执行算法后,根据不同情形给与委托策略
js
Validator.prototype.add = function (elem, rules = []) {
let self = this;
for (let i = 0; i < rules.length; i++) {
(function (rule) {
// 根据冒号传递参数
let strategyArr = rule.strategy.split(':');
let errMsg = rule.errMsg;
self.cache.push(function () {
// 策略名称
let strategy = strategyArr.shift();
strategyArr.unshift(elem.value);
strategyArr.push(errMsg);
// 分配策略算法
return strategies[strategy].apply(elem, strategyArr);
});
})(rules[i]);
}
};
代码如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>strategy</title>
</head>
<body>
<form action="http://liuzunkun.com" method="post" id="registerform">
<label for="name">账户 </label>
<input id="name" type="text" />
<label for="password">口令 </label>
<input id="password" type="password" />
<label for="tel">手机号 </label>
<input id="tel" type="tel" />
<button type="submit">注册系统</button>
<div id="errmsg"></div>
</form>
<script>
const strategies = {
isNotEmpty: function (value, errMsg) {
if (!value) return errMsg;
},
minLength: function (value, length, errMsg) {
if (!value || value.length < length) return errMsg;
},
isMobile: function (value, errMsg) {
const mobileReg = /^(13[0-9]|14[5-9]|15[0-3,5-9]|16[6]|17[0-8]|18[0-9]|19[8,9])\d{8}$/;
console.log({
value,
result: mobileReg.test(value),
});
if (!mobileReg.test(value)) return errMsg;
},
};
function Validator() {
this.cache = [];
}
Validator.prototype.add = function (elem, rules = []) {
let self = this;
for (let i = 0; i < rules.length; i++) {
(function (rule) {
let strategyArr = rule.strategy.split(':');
let errMsg = rule.errMsg;
self.cache.push(function () {
let strategy = strategyArr.shift();
strategyArr.unshift(elem.value);
strategyArr.push(errMsg);
console.log({ strategy, strategyArr });
return strategies[strategy].apply(elem, strategyArr);
});
})(rules[i]);
}
};
Validator.prototype.start = function () {
for (let i = 0; i < this.cache.length; i++) {
let validateFn = this.cache[i];
let errMsg = validateFn();
if (errMsg) return errMsg;
}
};
const registerform = document.getElementById('registerform');
function validateFn() {
const validator = new Validator();
validator.add(registerform.name, [
{
strategy: 'isNotEmpty',
errMsg: '账户不能为空',
},
{
strategy: 'minLength:6',
errMsg: '账户名长度不能<6',
},
]);
validator.add(registerform.password, [
{
strategy: 'isNotEmpty',
errMsg: '口令不能为空',
},
{
strategy: 'minLength:6',
errMsg: '口令名长度不能<6',
},
]);
validator.add(registerform.tel, [
{
strategy: 'isNotEmpty',
errMsg: '手机号不能为空',
},
{
strategy: 'isMobile',
errMsg: '请输入正确的手机号',
},
]);
return validator.start();
}
registerform.onsubmit = function (event) {
event.preventDefault();
let errMsg = validateFn();
if (errMsg) {
console.error(`验证失败`, errMsg);
document.getElementById('errmsg').innerHTML = errMsg;
return false;
}
document.getElementById('errmsg').innerHTML = '';
};
</script>
</body>
</html>
参考
- JavaScript 设计模式与开发实践