设计模式-策略模式

策略模式的定义

定义一系列的算法,把它们一个个封装起来,并使他们可以相互替换。

将可变的部分和变化的部分隔开,目的就是将算法的使用与算法的实现分离开。

“在函数作为一等对象的语言中,策略模式是隐形的。 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 中,使得它
    们易于切换,易于理解,易于扩展
  • 策略模式的算法可以使用在其他地方,复用性较强

缺点

  • 需要增加策略对象