JavaScript设计模式之策略模式

策略模式定义

我们在使用一些导航软件是,如果从 A 地到 B 地,导航软件会根据不同的条件使用不同的算法计算 A 地到 B 地的路径,但是最终要达到目的的一样的。这就是策略模式的体现。

策略模式就是定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换。

举个例子:在使用导航系统的时候,可以根据不同的条件去匹配不同的路径。那么可以这样去实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 路径算法类A
function PathPolicyA() {}
PathPolicyA.prototype.calculate = function (type) {
return "policyA";
};

// 路径算法类B
function PathPolicyB() {}
PathPolicyB.prototype.calculate = function (type) {
return "policyB";
};

// 路径类
function Path() {
this.path = null;
this.policy = null;
}

Path.prototype.setPolicy = function (policy) {
this.policy = policy;
};

// 根据不同类型获取不同的路径
Path.prototype.getPath = function () {
return this.policy.calculate();
};

const p = new Path();

p.setPolicy(new PathPolicyA());
console.log(p.getPath()); // policyA

p.setPolicy(new PathPolicyB());
console.log(p.getPath()); // policyB

上面代码是一个传统面向对象语言的策略模式实现,它通过setPolicy设置不同的策略,再通过getPath调用对应策略类的calculate计算方法,最终返回路径。

JavaScript 的策略模式

上面代码虽然实现了策略模式,但是上面代码在 JS 中实现似乎显得有点笨重。JavaScript 创建对象非常简答,而且对象里面可以是一个函数。这样只需定义一个对象就可以解决上面的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
var policyObj = {
A: () => "policyA",
B: () => "policyB",
};
function getPath(policyObj, type) {
if (policyObj[type] && typeof policyObj[type] === "function") {
return policyObj[type]();
}
return "policy is not found";
}

console.log(getPath(policyObj, "A")); // policyA
console.log(getPath(policyObj, "B")); // policyB

这段代码与上面的方法作用是一致的,而且这段代码看上去是更为简洁,我们只需定义一个对象装入所有的策略,在根据不同的条件去调用不同的方法。
上面代码都体现了,策略模式的核心:定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换

利用策略模式制作表单验证

从定义看来,似乎策略模式是用来封装算法的,其实不然,我们也可以用它来做一些扩展,封装一些业务规则,比如表单验证,下面就用策略模式来实现一个表单验证吧~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
var policy = {
// 必填校验
isEmpty: ({ val }) => !!val,
// 最大长度校验
maxLength: ({ maxLen, val }) => val.length < maxLen,
//纯数字校验
isNumber: ({ val }) => /^[0-9]*$/.test(val),
};

function varifyForm(formData, formRule) {
// 循环表单对象
for (let x in formData) {
// 字段有定义规则才进行验证
if (formRule[x]) {
const tmpVal = formData[x];
// 循环验证规则
for (let rule of formRule[x]) {
// 有定义的策略才验证
if (policy[rule.type]) {
const param = { ...Object.assign(rule.param || {}, { val: tmpVal }) };
const isAccept = policy[rule.type](param);
if (!isAccept)
return {
accept: false,
msg: rule.errMsg || `${x} varify fail`,
};
}
}
}
}
return {
accept: true,
msg: "",
};
}

// 定义测试用例
var form1 = {
a: null,
};
var form2 = {
a: 213213123,
};
var form3 = {
a: "d212",
};
var rules = {
a: [
{
type: "isEmpty",
errMsg: "a is Empty",
},
{
type: "maxLength",
errMsg: "a is too long",
param: { maxLen: 5 },
},
{
type: "isNumber",
errMsg: "a is not a number",
},
],
};

console.log(varifyForm(form1, rules)); // a is Empty
console.log(varifyForm(form2, rules)); // a is too long
console.log(varifyForm(form3, rules)); // a is not a number

上面代码中,policy是一个定义策略的对象,varifyForm验证表单的主方法,通过传入表单数据表单字段验证规则,验证每个字段所定义的规则。最终返回验证结果。

聊聊 JavsScript 中的策略模式

在 JavaScript 中,函数作为一等对象,策略模式在这里跟原型模式一样,它已经融入了 JavaScript 语言本身。虽然它在 JavaScript 中是一种相对“透明”的模式,但是了解策略模式对我们设计函数,了解 JavaScript 有一定的帮助。

使用策略模式也有一定有优点:

1. 利用委托,组合等技术,可以有效避免条件选择语句。
2. 算法独立封装在一个对象或类中,更容易切换,理解和扩展。

小结

本文主要介绍了策略模式的概念和优点,在 JavaScript 中使用策略模式的例子。

策略模式是定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换

JavaScript 设计模式其他文章
1.原型模式
2.设计模式之单例模式

若您觉得文章对你有帮助,可以点一个赞鼓励一下作者,若想了解更多 JavaScript 或者 node 知识可以点击这里

参考

  1. JavaScript 设计模式与开发实践