策略模式
定义:定义一系列算法,把它们一个个封装起来,并且可以互相替换
例如,我们要计算年终奖,年终奖根据绩效 A、B、C 来计算最终数值
实现
最初我们很容易想到用 分支 if 来解决这个问题,如果绩效 = A 则工资 x 2,如果绩效 = B 则工资 x 3
如果经常使用这样的分支结构,你会发现代码耦合度很高,很容易就出现一大坨代码堆砌在一起,只是 x 2 或者 x 3 不足以形成难以维护的结构,但如果不是 x 2 而是一个复杂的代码块,我们显然会想到封装里面的代码!
var performA = function (salary) {
return salary * 4;
};
var performB = function (salary) {
return salary * 3;
};
var performC = function (salary) {
return salary * 2;
};
var calcBonus = function (level, salary) {
if (level == "A") {
return performA(salary);
} else if (level == "B") {
return performB(salary);
} else if (level == "C") {
return performC(salary);
}
};
是的,虽然我们优化了代码,但没好到哪去,如果要添加一个 D 级,我们还是得堆砌代码
让我们来看看策略模式怎么做吧,策略模式让 策略 被定义和封装,且可以相互替换
这就是最终代码了,但在 javascript 中实现策略相较 C# 或者其他语言来说要容易的多,在下面举例了 C# 代码
var strategies = {
A: function (salary) {
return salary * 4;
},
B: function (salary) {
return salary * 3;
},
C: function (salary) {
return salary * 2;
},
};
var calculateBonus = function (level, salary) {
return strategies[level](salary);
};
需要注意的是 strategies 对象存储的 3 个匿名函数, Func 类是用来存储函数的,需要一定的函数工具类基础
掌握这样的思想以后,试着把 {"A", (salary) => salary * 4}
解耦出去动态添加即可~
using System;
using System.Collections.Generic;
public class Program
{
private static Dictionary<string, Func<double, double>> strategies = new Dictionary<string, Func<double, double>>()
{
{"A", (salary) => salary * 4},
{"B", (salary) => salary * 3},
{"C", (salary) => salary * 2}
};
private static double CalculateBonus(string level, double salary)
{
return strategies[level](salary);
}
public static void Main(string[] args)
{
string level = "A";
double salary = 1000;
double bonus = CalculateBonus(level, salary);
Console.WriteLine("Bonus: " + bonus);
}
}
思想
通过上面的重构:
- 消除了大片的分支语句
- 计算奖金的逻辑不再存储在 CalculateBonus 里了,而是分布在策略对象里
- 策略对象只负责计算奖金
- 策略对象之间可以相互替换
实战 - 表单
这是一种尤为常见的表单验证方式,相信绝大多数前端程序员这样写过
显然能发现,这里的 if 堆砌过多,不仅如此,内部的 逻辑 相比上面的代码也更复杂
var registerForm = function (form) {
if (form.username.value === "") {
alert("用户名不能为空");
return false;
}
if (form.password.value.length < 6) {
alert("密码长度不能少于6位");
return false;
}
if (!/(^1[3|5|8][0-9]{9}$)/.test(form.phoneNumber.value)) {
alert("手机号码格式不正确");
return false;
}
};
我们可以用策略模式的思路来实现类似这样的代码,这样当我们需要增加验证步骤时,只需要添加策略内容即可:
var validateStrategy = {
isNotEmpty: function (form) {
if (form.name === "") {
return "用户名不能为空";
}
return "";
},
minLength: function (form) {
if (form.password.length < 6) {
return "密码长度不能少于6位";
}
return "";
},
isMobile: function (form) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(form.phone)) {
return "手机号码格式不正确";
}
return "";
},
};
var validate = function (form) {
for (let func in validateStrategy) {
if (
validateStrategy.hasOwnProperty(func) &&
typeof validateStrategy[func] === "function"
) {
var msg = validateStrategy[func](form);
if (msg != "") return false;
}
}
return true;
};