如果感觉文章不错,欢送关注、点赞和分享!

继续分享技术博文,关注微信公众号  前端LeBron

很快,迎来了 JavaScript 设计模式系列的第二篇 —— 策略模式 ...

什么是策略模式

策略模式定义:

定义一系列算法,把它们一个个封装起来,并且使它们能够互相替换

策略模式个别由两局部组成:

  1. 封装不同策略的策略组(使得代码复用性、可扩大、可维护性进步,防止大量 CV 代码的状况)
  2. Context(委托算法,执行策略)

什么时候应用策略模式 ?

策略模式广泛应用于程序研发中,当呈现须要依据不同的前置条件执行不同的算法失去后果时,应用策略模式能够让你的代码更加优雅

怎么?不信? 那就上点代码让你感受一下 山的力量!

假如一个函数负责 Consul (服务发现)和 LB (负载平衡)

这里 Consul 函数就是 Context,各种 LB 算法就是策略组

传入服务惟一标识和负载平衡算法

返回服务器实例 IP 地址

如想理解 LB 相干常识,能够看看这篇文章 [深入浅出LB]手把手带你实现一个负载均衡器

function consul(serviceId, algorithm) {  if (algorithm === 'random') {    // random flow         // ...         return 'xxx.xxx.xxx.xxx';  } else if (algorithm === 'weightedRoundRobin') {    // weightedRoundRobin flow         // ...         return 'xxx.xxx.xxx.xxx';  } else if (algorithm === 'ipHash') {    // ipHash flow         // ...         return 'xxx.xxx.xxx.xxx';  } else if (algorithm === 'urlHash') {    // urlHash flow         // ...         return 'xxx.xxx.xxx.xxx';  } else if (algorithm === 'leastConnection') {    // leastConnection flow         // ...         return 'xxx.xxx.xxx.xxx';  } else if (algorithm === 'fair') {    // fair flow         // ...         return 'xxx.xxx.xxx.xxx';  }}

能够发现,这段代码存在着一些显著的问题

  1. Consul 函数过于宏大,不合乎繁多职责准则,难保护
  2. 堆砌了过多的 IF-ELSE 语句,代码看起来比拟冗余
  3. 负载平衡算法复用性较差,如果其余中央须要用到...(你不会间接 CV 吧

针对以上问题,咱们能够利用策略模式加以优化
  1. 首先将各 LB 算法封装成独立的函数,进步复用性
  2. 建设算法名称 -> 算法执行函数映射,干掉冗余的 IF-ELSE(几乎就是 IF-ELSE 的救世主
  3. 简化 Consul 函数,具体算法对于 Consul 来说是“隐形”的,繁多职责
  4. 可拓展性进步,如需拓展更多算法,仅需引入算法和增加 Map 中的配置
function random(serviceId) {  // random flow     // ...     return 'xxx.xxx.xxx.xxx';}function weightedRoundRobin(serviceId) {  // weightedRoundRobin flow     // ...     return 'xxx.xxx.xxx.xxx';}function ipHash(serviceId) {  // IPHash flow     // ...     return 'xxx.xxx.xxx.xxx';}function urlHash(serviceId) {  // URL Hash flow     // ...     return 'xxx.xxx.xxx.xxx';}function leastConnection(serviceId) {  // leaseConnection flow     // ...     return 'xxx.xxx.xxx.xxx';}function fair(serviceId) {  // fair flow     // ...     return 'xxx.xxx.xxx.xxx';}const ALGORITHM = {  RANDOM: 'random',  WEIGHTED_ROUND_ROBIN: 'weightedRoundRobin',  IP_HASH: 'ipHash',  URL_HASH: 'urlHash',  LEAST_CONNECTION: 'leastConnection',  FAIR: 'fair',};const ALGORITHM_MAP = {  [ALGORITHM.RANDOM]: random,  [ALGORITHM.WEIGHTED_ROUND_ROBIN]: weightedRoundRobin,  [ALGORITHM.IP_HASH]: ipHash,  [ALGORITHM.URL_HASH]: urlHash,  [ALGORITHM.LEAST_CONNECTION]: leastConnection,  [ALGORITHM.FAIR]: fair,};function consul(serviceId, algorithm) {  return ALGORITHM_MAP[algorithm](serviceId);}

策略模式利用场景之表单校验

毛糙的表单校验

// 毛糙的表单校验let loginFrom = document.getElementId("loginFrom")loginFrom.onsubmit = function (e) {    const username =  document.getElementId("username")    const pwd =  document.getElementId("pwd")    const mobile =  document.getElementId("mobile")    if (username === '') {        alert("用户名不可为空")        return false    }     if (pwd.length < 6) {        alert("明码长度不能少于6位")        return false    }    if (!/^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/.test(mobile)) {        alert("手机号码格局不正确")        return false    }}

应用策略模式优化后的表单校验

Context:validate

策略组:strategy

// 应用策略模式优化const strategy = {  isEmpty: function ({ value }, errMsg) {    if (value === '') {      return errMsg;    }  },  isMobile: function ({ value }, errMsg) {    if (      !/^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/.test(value)    ) {      return errMsg;    }  },  isLongerThanMinLength: function ({ value, len }, errMsg) {    if (value.length < len) {      return errMsg;    }  },};class Validator {  constructor() {    this.validateItems = [];  }  add(params, strategyKey, errMsg) {    this.validateItems.push({ params, strategyKey, errMsg });  }  start() {    for (const item of this.validateItems) {      const { params, strategyKey, errMsg } = item;      const msg = strategy[strategyKey](params, errMsg);      if (msg) {        return msg;      }    }  }}function validate() {  const username = document.getElementId('username');  const pwd = document.getElementId('pwd');  const mobile = document.getElementId('mobile');  const validator = new Validator();  validator.add({ value: username }, 'isEmpty', '用户名不可为空');  validator.add(    { value: pwd, len: 6 },    'isLongerThanMinLength',    '明码长度不能少于6位'  );  validator.add({ value: mobile }, 'isMobile', '手机号码格局不正确');  const errMsg = validator.start();  if (errMsg) {    alert(errMsg);  } else {    alert('校验通过');  }}validate();

策略模式和多态的区别

  • 策略模式定义了算法组,别离封装,它们之间能够互相替换,此模式的变动独立于应用算法的客户
  • 多态罕用继承、办法重写、父类援用指向子类对象等办法实现

策略模式强调的是做同一件事的不同且不反复的办法

多态是一种语言机制,有的不反对多态的语言也一样要实现策略模式

策略处于程序设计档次,多态处于语言语法档次

总结

策略模式的长处

  • 策略模式利用组合、委托和多态等技术和思维,能够无效防止多重且冗余的 IF-ELSE
  • 策略模式提供了对凋谢——关闭准则的完满反对,将算法封装在独立的策略中。能够在不批改原代码的状况下,灵便减少新算法。进步了它们的复用性、和可拓展性,也更容易切换和了解。
  • 策略模式中的算法也能够复用在工程的其余中央,防止大量反复的 CV 工作
  • 在策略模式中利用组合和委托来让 Context 领有执行算法的能力,这也是继承的一种更轻便的代替计划

策略模式的毛病

  • 策略模式会在程序中减少许多策略函数、类、对象,但实际上比把它们堆砌在 Context 中要更好
  • 应用策略模式必须理解所有的策略,必须理解它们的细节比拟它们之间的不同点,能力抉择一个适合的策略。此时须要向用户裸露它的所有实现,违反起码常识准则。

设计模式系列往期文章

  • JavaScript 设计模式 —— 单例模式
继续分享技术博文,欢送关注!
  • 微信公众号:前端LeBron
  • 掘金:前端LeBron
  • 知乎:前端LeBron