设计模式-策略模式
策略模式的定义
定义一系列的算法,把它们一个个封装起来,并使他们可以相互替换。
将可变的部分和变化的部分隔开,目的就是将算法的使用与算法的实现分离开。
“在函数作为一等对象的语言中,策略模式是隐形的。 strategy 就是值为函数的变量。”
策略模式应用场景
优化判断语句
举一个简单的例子:
后端返回一个 status 字段,如果是 0,就代表停用,如果是 1,就代表启用,要求前端在页面上显示出相应文案。
/* 解法一:三元运算 */
const text = status === 0 ? "停用" : "启用";
document.getElementById("root").innerText = "text";
/* 解法二:if判断 */
function getText(status) {
  if (status === 0) {
    return "停用";
  } else if (status === 1) {
    return "启用";
  } else {
    return null;
  }
}
document.getElementById("root").innerText = getText(status);
/* 解法三:switch语句*/
// 与if区别不大,就不做演示了
我们很轻松地解决了这个问题,如果 status 的可能的值不再是 0 或 1,而是 0-9,分别代表某个流程的 10 个步骤,对应 10 个不同的文案,上面的写法就有点麻烦了。
可能会变成下面的样子:
function getText(status) {
  if (status === 0) {
    return "文案0";
  } else if (status === 1) {
    return "文案1";
  } else if (status === 2) {
    // ...
  }
  // ...
}
document.getElementById("root").innerText = getText(status);
这样编写存在的问题:
- if-else 语句需要覆盖所有逻辑分支
 - 缺乏弹性,新增一种文案,则要 getText 方法内新增一句 if-else
 
我们要使用策略模式优化
// 定义一个策略对象
const strategies = {
  0: "文案0",
  1: "文案1",
  2: "文案2",
  // ...
  9: "文案9"
};
// 通过输入[key]的不同,选择不同的策略算法[value]
const text = strategies[status];
document.getElementById("root").innerText = text;
此处要进行的操作比较简单,根据不同的 status 返回不同普通的 text。
如果是针对 status 进行不同的操作,我们将字符串换成相应的操作方法即可。
// 定义一个策略对象
const strategies = {
  0: function() {},
  1: function() {},
  2: function() {},
  2: function() {}
  // ...
};
strategies[status] && strategies[status]();
这里是用的是匿名函数,如果将不同条件对应的算法抽出来定义成一个普通函数,算法的复用性也得到了增强。
表单校验
在一个 Web 项目中,注册、登录、修改用户信息等功能的实现都离不开提交表单。
在将用户输入的数据交给后台之前,常常要做一些客户端力所能及的校验工作,比如注册的 时候需要校验是否填写了用户名,密码的长度是否符合规定,等等。这样可以避免因为提交不合 法数据而带来的不必要网络开销。
假设我们正在编写一个注册的页面,在点击注册按钮之前,有如下几条校验逻辑。
- 用户名不能为空。
 - 密码长度不能少于 6 位。
 - 手机号码必须符合格式。
 
使用 if-else 进行表单校验
<html lang="en">
  <head>
    <title>使用判断语句进行表单校验</title>
  </head>
  <body>
    <form action="http://xxx.com/register" id="registerForm" method="post">
      请输入用户名: <input type="text" name="userName" /> 请输入密码:
      <input type="text" name="password" />请输入手机号码:
      <input type="text" name="phoneNumber" /> <button>提交</button>
    </form>
    <script>
      var registerForm = document.getElementById("registerForm");
      registerForm.onsubmit = function() {
        if (registerForm.userName.value === "") {
          alert("用户名不能为空");
          return false;
        }
        if (registerForm.password.value.length < 6) {
          alert("密码长度不能少于 6 位");
          return false;
        }
        if (!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
          alert("手机号码格式不正确");
          return false;
        }
      };
    </script>
  </body>
</html>
- registerForm.onsubmit 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有的校验规则。
 - registerForm.onsubmit 函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长度校验从 6 改成 8,我们都必须深入 registerForm.onsubmit 函数的内部实现,这是违反开放—封闭原则的。
 - 算法的复用性差,如果在程序中增加了另外一个表单,这个表单也需要进行一些类似的校验,那我们很可能将这些校验逻辑复制得漫天遍野
 
使用策略模式进行优化
策略模式的优缺点
优点
- 策略模式可以有效避免多重条件选择语句
 - 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它
们易于切换,易于理解,易于扩展 - 策略模式的算法可以使用在其他地方,复用性较强
 
缺点
- 需要增加策略对象