介绍
策略模式是一种行为设计模式, 它能让你定义一系列算法, 把它们一个个封装起来, 并使它们可以相互替换。
常规写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>表单检验</title>
</head>
<body>
<form
action=""
id="registerForm"
method="post"
onsubmit="return submitValidate()"
>
<p>
<label>请输入用户名:</label>
<input type="text" name="userName" />
</p>
<p>
<label>请输入密码:</label>
<input type="text" name="password" />
</p>
<p>
<label>请输入手机号码:</label>
<input type="text" name="phoneNumber" />
</p>
<div>
<button type="submit">提交</button>
</div>
</form>
<script>
function submitValidate() {
var registerForm = document.getElementById("registerForm");
if (registerForm.userName.value === "") {
alert("用户名不能为空");
return false;
} else if (registerForm.password.value.length < 6) {
alert("密码的长度不能小于6位");
return false;
} else if (!/(^1[0-9]{10}$)/.test(registerForm.phoneNumber.value)) {
alert("手机号码格式不正确");
return false;
}
return true;
}
</script>
</body>
</html>
这是一种很传统的代码编写方式,但它有许多缺点:
- 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有的校验规则。
- 函数缺乏弹性,如果增加了一种新的校验规则,比如想把密码的长度从6改成8,我们都必须深入函数的内部实现,这是违反开放-封闭原则的。
- 算法的复用性差,如果在项目中增加了另外一个表单,这个表单也需要进行一些类似的校验,我们很可能将这些校验逻辑复制得漫天遍野。
策略模式
在实际开发中,我们通常会把算法的含义扩散开来,使策略模式也可以用来封装一系列的“业务规则”。只要这些业务规则指向的目标一致,并且可以被替换使用,我们就可以用策略模式来封装它们。
一个策略模式至少由两部分组成。
- 第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。
- 第二个部分是环境类,接受客户的请求,随后把请求委托给某一个策略类
定义规则(策略)
const strategies = {
isNotEmpty: function (value, errMsg) {
if (value === '') {
return errMsg;
}
},
minLength: function (value, length, errMsg) {
if (value.length < length) {
return errMsg;
}
},
isMobile: function (value, errMsg) {
if (!/^1[2-9][0-9]{9}$/.test(value)) {
return errMsg;
}
}
}
定义环境类
class Validator {
constructor() {
this.cache = [];
}
register(value, rule, errMsg) {
const arr = rule.split(':');
this.cache.push(() => {
const strategy = arr.shift();
arr.unshift(value);
arr.push(errMsg);
return strategies[strategy].apply(value, arr);
})
}
validate() {
for (let i = 0; i < this.cache.length; i++) {
const msg = this.cache[i]();
if (msg) return msg;
}
}
}
function submitValidate() {
var form = document.getElementById("registerForm");
const data = {
userName: form.userName.value,
password: form.password.value,
phoneNumber: form.phoneNumber.value
};
const validator = new Validator();
validator.add(data.userName, "isNotEmpty", "用户名不能为空");
validator.add(data.password, "minLength:6", "密码长度不能少于6位");
validator.add(data.phoneNumber, "isMobile", "手机号码格式不正确");
const errMsg = validator.validate();
if (errMsg) {
alert(errMsg);
return false;
}
}
使用策略模式之后,消除了原程序中大片的条件分支语句。仅仅通过“配置”的方式就可以完成一个表单校验,这些校验规则也能在程序中任何地方复用
策略模式优点:
- 可以有效地避免多重条件选择语句。
- 对开放-封闭原则完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展。
- 可以使算法复用在系统的其他地方,避免许多重复的复制粘贴工作
缺点:
- 使用策略模式会在程序中增加许多策略类或策略对象
- 要使用策略模式,必须了解所有的 strategy,了解它们的不同点,我们才能选择一个合适的 strategy。这是违反最少知识原则的
最后
1、策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象,从而以间接方式在运行时更改对象行为。
2、策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个,从而减少重复代码。
3、策略模式让你能将各种算法的代码、内部数据和依赖关系与其他代码隔离开来。不同客户端可通过一个简单接口执行算法,并能在运行时进行切换。
4、策略模式将所有继承自同样接口的算法抽取到独立类中,因此不再需要条件语句。原始对象并不实现所有算法的变体,而是将执行工作委派给其中的一个独立算法对象
策略模式虽然较于传统模式更方便,但是实际应用中还是不能满足很多场景,所以后续会针对表单校验场景的能力进行一次升级