所谓策略,就是根据已知条件决定要做出怎样的行为。
举个栗子:我要实现一个表单校验功能,要求 name
不能为空且长度必须大于 2 且小于 4,age
不能为空且必须为纯数字。
这样的判断逻辑直接用 if-else
就可以实现:
function valid() {
// 表单校验逻辑
if (!form.name) {
alert("name 不能为空");
return false;
}
if (form.name.length < 2) {
alert("name 长度必须大于 2 ");
return false;
}
if (form.name.length > 4) {
alert("name 长度必须小于 4");
return false;
}
if (!form.age) {
alert("age 不能为空");
return false;
}
if (!/^\d+$/.test(form.age)) {
alert("age 只能为数字");
return false;
}
return true;
}
这种实现方式有几个缺点:
valid()
函数比较庞大,并且包含了很多 if-else
语句,需要在这里覆盖所有的校验规则。策略模式就是为了解决这些问题而生的。
策略模式的核心思想是将算法与使用对象分开,对象不是直接实现单个算法,而是在执行时决定要使用哪些算法。
它基于组合优于继承的原则,通过对一系列算法的封装,使它们在运行时可以互换。
这使得对象更加灵活和可重用,因为可以轻松添加或修改不同的策略,而无需更改对象的核心代码。
以上面的表单校验逻辑为例,这段代码中有四个策略: 字段不能为空
、字段长度必须大于x
、字段长度必须小于x
、字段只能为数字
。
优化思路:
在 Validator
类中封装所有校验策略,并提供通用的使用接口。
class Validator {
/**
* 内置校验策略
*/
static strategy = {
require(value, errorMsg) {
if (value === "" || value === undefined || value === null) {
return errorMsg;
}
},
minLength(value, errorMsg, length) {
if (value.length < length) {
return errorMsg;
}
},
maxLength(value, errorMsg, length) {
if (value.length > length) {
return errorMsg;
}
},
number(value, errorMsg) {
if (!/^\d+$/.test(value)) {
return errorMsg;
}
},
};
rules = [];
/**
* 添加一条校验规则
* @param {*} value 待校验的值
* @param {*} validFn 校验方法
* @param {*} errorMsg 错误信息
*/
add(value, validFn, errorMsg, ...args) {
if (typeof validFn === "function") {
this.rules.push(validFn.bind(this, value, errorMsg, ...args));
} else {
console.error("validFn 必须是一个函数");
}
}
/**
* 启动校验
*/
valid() {
let length = this.rules.length;
while (length--) {
const rule = this.rules.shift();
const errorMsg = rule();
if (errorMsg) {
return errorMsg;
}
}
}
}
业务逻辑中的使用方式如下:
// 校验逻辑,返回值为错误信息,无返回值表示校验通过
function valid() {
const validator = new Validator();
// 给 name 设置校验规则
validator.add(form.name, Validator.strategy.require, "name 不能为空");
validator.add(
form.name,
Validator.strategy.minLength,
"name 长度必须大于 2",
2
);
validator.add(
form.name,
Validator.strategy.maxLength,
"name 长度必须小于 4",
4
);
validator.add(form.age, Validator.strategy.require, "age 不能为空");
validator.add(form.age, Validator.strategy.number, "age 只能为数字");
// 校验
return validator.valid();
}
使用策略模式重构表单校验逻辑后,可以通过配置的方式就完成一个表单的校验,这些校验规则也可以更方便的复用。
策略模式的优点是提高代码的灵活性和可复用性。
它的缺点是使用者必须了解所有的策略,才能选择合适的策略。
如果逻辑比较简单,使用 if-else
足以应对,就没必要用策略模式。
《JavaScript 设计模式与开发实践》