阅读 248

JavaScript设计模式—策略模式

定义

定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。一个基于策略模式的程序至少有两部分组成:

  1. 策略类:封装具体的算法,并负责具体的计算过程
  2. 环境类 Context: Context接受客户请求,随后将请求委托给策略类

真实世界类比

策略模式

假如你需要前往机场。 你可以选择乘坐公共汽车、 预约出租车或骑自行车。 这些就是你的出行策略。 你可以根据预算或时间等因素来选择其中一种策略。

模式实现-计算奖金

这里我们以年终奖的计算为例进行实现:

假设绩效为S的人年终奖有 4 倍工资;A的人年终奖有 3 倍工资;B的人年终奖有 2 倍工资。

按照策略模式的基本组成我们进行分解:

// 第一步: 封装策略类
const strategies = {
  S: salary => {
    return salary * 4;
  },
  A: salary => {
    return salary * 3;
  },
  B: salary => {
    return salary * 2;
  }
};

// 第二步:这里我们用calculateBonus函数充当Context来接收用户请求
const calculateBonus = (level, salary) => {
  return strategies[level](salary);
};

console.log(calculateBonus("S", 20000)); // 输出:80000
console.log(calculateBonus("A", 10000)); // 输出:30000
复制代码

更广义的“算法”

策略模式指的是定义一系列算法。但在实际的开发中,我们通常会把算法的含义扩散开来,使策略模式也可以用来封装一系列的业务规则。只要这些业务规则指向的目标一致,而且可以被替换使用。

模式实现-表单校验

在 web 应用中,涉及表单的部分几乎都离不开校验。

假设我们正在编写一个注册页面,在点击注册按钮前,需要校验以下几条逻辑:

  • 用户名不能为空
  • 密码长度不少于 6 位
  • 手机号必须符合格式
// 第一步: 封装策略类
const strategies = {
  isNonEmpty: (value, errorMsg) => {
    if (value === "") {
      return errorMsg;
    }
  },
  minLength: (value, errorMsg) => {
    if (value.length < 6) {
      return errorMsg;
    }
  },
  isMobile: (value, errorMsg) => {
    if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg;
    }
  }
};

// 第二步:封装Validator类充当Context来接收用户请求并委托给strategy对象
class Validator {
  constructor() {
    this.cache = [];
  }
  add(dom, strategy, errorMsg) {
    this.cache.push(() => {
      const args = [dom.value, errorMsg];
      return strategies[strategy].apply(dom, args);
    });
  }
  start() {
    this.cache.forEach(fn => {
      const msg = fn();
      if (msg) {
        return msg;
      }
    });
  }
}

// 使用
const registerForm = document.getElementById("registerForm");
function validataFunc() {
  const validator = new Validator();
  validator.add(registerForm.userName, "isNonEmpty", "用户名不能为空");
  validator.add(registerForm.password, "minLength", "密码长度不能少于6位");
  validator.add(registerForm.phone, "isMobile", "手机号码格式不正确");
  const error = validator.start();
  return error;
}
registerForm.onsubmit = () => {
  const errorMsg = validataFunc();
  if (errorMsg) {
    alert(errorMsg);
    return false;
  }
};
复制代码

可以看到,我们仅仅通过配置的方式就完成了一个表单的校验。

小结

在JavaScript语言的策略模式中,策略类往往被函数所代替,这时策略模式就成了一种“隐形”的模式。尽管如此,从根本上理解策略模式的理念不仅让我们对模式有这更透彻的了解,也可以让我们明白使用函数的好处。

参考