策略模式是什么 ?

策略可以说成是一种计划,通常情况下,我们都存在很多种选择。

以过年回家为例子,我们回家可以选择不同的交通方式。

  • 自驾🚗
  • 飞机✈️
  • 火车🚄

而这三种都有不同的代价,往往我们需要结合自身情况进行选择。

我们用js代码实现选择函数帮助人们选择。假设,这些交通方式和宠物有关。

  • dog:🚗
  • cat:✈️
  • panda:🚄
// if-else 语句
const getTrafficMethod = (petType) => {
  if(petType === "dog") {
    // 自驾回家需要为宠物狗准备东西的逻辑
  } else if (petType === "cat") {
    // 坐飞机需要对宠物猫准备的逻辑
  } else if (petType === "panda") {
    // 坐火车对熊猫准备的逻辑
  }
};

我们很容易写出这样的逻辑。但是这样写真的好吗?我们假设这样一个场景,我们有条宠物蛇需要坐船🚢呢。难道,我们又直接添加一个 else if 语句。这样合理吗?很显然有些不合理。因为这违反了 开放-封闭原则。其次,如果我们其他地方也需要对这些宠物回家的处理逻辑。我们很显然不想一个个又拷贝一遍。

什么是开放封闭原则呢 ? 只能够扩展原来的代码,而不能修改。也就是说,上面的代码中,我们并不能在函数内部修改。当代码量极其大的时候,我们修改就很可能会出现bug。为了避免这种 if-else 且符合开放封闭原则,我们引出策略模式

策略模式的作用 ?

我们利用策略模式将上面代码优化。

const petTypeMapStrategy = {
  "dog": function() {
    // 自驾回家需要为宠物狗准备东西的逻辑
  },
  "cat": function() {
    // 坐飞机需要对宠物猫准备的逻辑
  },
  "panda": function() {
    // 坐火车对熊猫准备的逻辑
  }
};


const getTrafficMethod = (petType) => {
  return petTypeMapStrategy[petType]();
};

可以看到,上面把对应宠物执行逻辑,从函数中抽离,实现了解耦。同时,当我们需要新增加一个宠物的处理的逻辑,直接在对象中修改即可,而不用修改函数中的代码。其次,我们能够很方便的在其他地方复用这些逻辑。

综上所诉,策略模式的好处如下两点:

  • 代码解耦合,符合开放封闭原则。
  • 代码逻辑方便复用。

使用策略模式设计表单验证

我们假设有两个字段,name、age 当我们点击提交按钮的时候,我们想要去验证它们是否为空,和是否满足某些条件。

于是,我们很容易写下了如下代码。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>策略模式设计表单验证</title>
</head>
<body>
  name: <input id="name" type="text" />
  age: <input id="age" type="number" name="" id="">
  <button id="submit">submit</button>

  <script>
    // 1. 当我们点击提交的时候需要验证字段是否为空
    const nameInputEl = document.querySelector("#name");
    const ageInputEl = document.querySelector("#age");
    const submitEl = document.querySelector("#submit");

    submitEl.addEventListener("click", function() {
      // 取得对应字段值
      const formData = {
        name: nameInputEl.value,
        age: ageInputEl.value
      };

      // 验证字段是否为空
      if (!formData.name) {
        // 字段为空的逻辑
      } else if (formData.name.length > 6) {
        // 处理字段长度是否满足条件
      }

      if (!formData.age) {
        // 字段为空的逻辑
      } else if (formData.age > 10) {
        // 字段是否满足条件的逻辑
      }

      // ... 字段满足条件发起请求,字段不满足条件则提醒。
    });
  </script>
</body>
</html>

但是,我们可以考虑,当我们新加一个字段是不是又会写重复的判断逻辑。有没有一种方式简化这些逻辑呢?这个时候 策略模式 来救场了。

我么可以将校验规则看成一个个策略,将策略从函数中抽离到对象中去。我们修改代码如下。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>策略模式设计表单验证</title>
</head>
<body>
  name: <input id="name" type="text" />
  age: <input id="age" type="number" name="" id="">
  <button id="submit">submit</button>

  <script>
    // 1. 当我们点击提交的时候需要验证字段是否为空
    const nameInputEl = document.querySelector("#name");
    const ageInputEl = document.querySelector("#age");
    const submitEl = document.querySelector("#submit");

    // stragety
    const rules = {
      isRequire: (val) => {
        // 判断val是否为空
      },
      minLength: (val) => {
        // val 是否满足最小长度
      }
    };

    // 校验器
    const valider = ({val, inRules}) => {
      const msg = [];
      inRules.forEacth(rule => {
        const [ruleName, ...args] = rule.split(":");
        const res = rules[ruleName].apply(this, args);
        if (res)
          msg.push(res);
      });
      return msg;
    } 

    submitEl.addEventListener("click", function() {
      // 取得对应字段值
      const formData = {
        name: {
          value: nameInputEl.value,
          rules: ["isRequire", "minLength:6"]
        },
        age: {
          value: ageInputEl.value,
          rules: ["isRequire", "minLength:6"]
        }
      };
      const msg = {};
      for (const key of Reflect.ownKeys(formData)) {
        const res = valider(formData[key]);
        if (res.length !== 0)
          msg[key] = res;
      }
      if (Reflect.ownKeys(msg).length === 0) {        
        // 发起请求
      }
      else {
        //  提示用户错误消息
      }

    });
  </script>
</body>
</html>

上面代码中,我们将验证字段的逻辑从函数中抽离,实现了逻辑复用。同时,我们判断新字段在也不用修改函数內的代码了。自需要为其添加rules配置即可,符合 开放封闭原则