前端设计模式-策略模式

欢迎访问主页,有更多文章内容
转载请注明原出处
原文链接地址:前端设计模式-策略模式

前言

策略模式是定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。将不变的部分和变化的部分隔开是每个设计模式的主题。策略模式的目的就是使算法的使用与算法分离开来。封装的算法具有一定的独立性,不会随客户端的变化而变化。

一个策略模式的程序至少有两部分组成。第一部分是策略类,策略类封装了具体的算法,负责具体的计算过程。 第二部分是环境类Context,它接受了客户的要求,随后吧请求委托给某一具体的策略类。要做到这点,Context中要维持对摸个策略对象的引用。

例子

这个 demo 是一个计算的代码段

function count(type, number1, number2) {
  switch (type) {
    case "add":
      return number1 + number2;
    case "subtract":
      return number1 - number2;
    case "multiply":
      return number1 * number2;
    default:
      return number1 / number2;
  }
}
// if else
function count1(type, number1, number2) {
  if (type === "add") {
    return number1 + number2;
  } else if (type === "subtract") {
    return number1 - number2;
  } else if (type === "multiply") {
    return number1 * number2;
  } else {
    return number1 / number2;
  }
}

或者这中间的switchif...else...来实现,但switchif...else...更加一目了然。这段计算方法本身没有问题,但是有更加便于维护和扩展的实现方式。下面来进行改进

const count = {
  add(number1, number2) {
    return number1 + number2;
  },
  subtract(number1, number2) {
    return number1 - number2;
  },
  multiply(number1, number2) {
    return number1 * number2;
  },
  divide(number1, number2) {
    return number1 / number2;
  },
};

count.add(2, 3); // 5

count.subtract(10, 3); // 7

count.multiply(2, 3); // 6

count.divide(6, 2); // 3

经过改造后,可以看成是一个策略模式,接下来看个复杂的例子。商场的会员分普通会员、银卡会员、金卡会员、钻石会员、至尊会员等级别,在商场消费享不同的折扣和积分。会员在同一商品的折扣分别是 98、95、9、8.8、8.5 折优惠。积分比例分别是 1、1.2、1.5、1.6、1.8。购买商品对应的会员折扣和积分获取用策略模式来实现:

const priceStrategy = {
  level1(price) {
    return price * (98 / 100);
  },
  level2(price) {
    return price * (95 / 100);
  },
  level3(price) {
    return price * (90 / 100);
  },
  level4(price) {
    return price * (88 / 100);
  },
  level5(price) {
    return price * (85 / 100);
  },
};

const cumulativeScoreStrategy = {
  level1(price) {
    return Math.floor(price);
  },
  level2(price) {
    return Math.floor(price * 1.1);
  },
  level3(price) {
    return Math.floor(price * 1.2);
  },
  level4(price) {
    return Math.floor(price * 1.3);
  },
  level5(price) {
    return Math.floor(price * 1.4);
  },
};

function calculatePrice(level, price) {
  return priceStrategy[level] ? priceStrategy[level](price) : 0;
}

function calculateScore(level, price) {
  const actually = calculatePrice(level, price);
  return (
    cumulativeScoreStrategy[level] && cumulativeScoreStrategy[level](actually)
  );
}

//  一台的彩电的价格是 8888,钻石会员的折后价
calculatePrice("level4", 8888); // 7554.8
// 获得的积分
calculateScore("level4", 8888); // 10567

表单验证的策略模式

const strategies = {
  isCorrectPassword(value, errorMsg) {
    if (!/^(?:(?=.*[A-Z])(?=.*[0-9])).\\S{7,19}$/.test(value)) {
      return errorMsg;
    }
    return null;
  },
  isNotEmpty(value, errorMsg) {
    if (value === "" || value === undefined) {
      return errorMsg;
    }
    return null;
  },
  minLength(value, length, errorMsg) {
    if (value.length < length) {
      return errorMsg;
    }
    return null;
  },
  maxLength(value, length, errorMsg) {
    if (value.length > length) {
      return errorMsg;
    }
    return null;
  },
  isMobile(value, errorMsg) {
    if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg;
    }
    return null;
  },
  isNotAllEmpty(value = [], errorMsg) {
    if (!value.some((i) => !!i)) {
      return errorMsg;
    }
    return null;
  },
};

class Validator {
  constructor() {
    this.cache = [];
  }

  add(value, rules) {
    for (let i = 0, rule; (rule = rules[i++]); ) {
      const strategyArray = rule.strategy.split(":") || [];
      const { errorMsg } = rule;
      this.cache.push(() => {
        const strategy = strategyArray.shift();
        strategyArray.unshift(value);
        strategyArray.push(errorMsg);
        return strategies[strategy].apply(value, strategyArray);
      });
    }
  }

  run() {
    for (let i = 0, validatorFunc; (validatorFunc = this.cache[i++]); ) {
      const msg = validatorFunc();
      if (msg) {
        return msg;
      }
    }
    return null;
  }
}

// 使用
const validator = new Validator();
validator.add(registerForm.userName, [
  {
    strategy: "isNonEmpty",
    errorMsg: "userName not empty!",
  },
  {
    strategy: "minLength:6",
    errorMsg: "userName length should more than 6",
  },
]);
validator.add(registerForm.password, [
  {
    strategy: "minLength:6",
    errorMsg: "password length should more than 6",
  },
]);

const errorMsg = validator.start();

if (errorMsg) {
  console.log(errorMsg);
  return errorMsg;
}

策略模式属于对象行为型模式,主要针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响 到客户端的情况下发生变化。通常,策略模式适用于当一个应用程序需要实现一种特定的服务或者功能,而且该程序有多种实现方式时使用。

策略模式的优缺点

策略模式是一种常用且有效的得设计模式,

优点

· 减少重复代码 策略模式利用组合、委托和多态等技术和思想,减少很多模板代码。

· 扩展性 策略模式提供了对开放-封闭原则的完美支持,将算法封装在独立的策略类里面,使它们易于切换,易于理解,易于扩展。

· 在策略模式中的算法利用组合和委托让Context拥有执行算法的能力,这也是继承的一种更轻便的替换方案。

缺点

· 使用策略模式会的程序增加了许多策略类或者策略对象,但实际上这比把他们负责的逻辑堆砌在Context中要更好。

· 要使用策略模式,必须了解所有的策略,必须了解各个策略之间的不同点,因为各种算法之间相互独立,对于复杂的算法处理相同的逻辑无法实现共享。

参考

本文参考<<javascript 设计模式与开发实践>> 推荐阅读。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容

  • 行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任...
    放下梧菲阅读 281评论 0 1
  • 目录 本文的结构如下: 引言 什么是策略模式 模式的结构 典型代码 代码示例 策略模式和模板方法模式的区别 优点和...
    w1992wishes阅读 852评论 1 7
  • 策略模式的介绍 ​ 在实际开发过程中,我们常常遇到这样的问题,实现某一个功能可以有多种算法或者策略,我们根据实...
    Android天之骄子阅读 451评论 0 0
  • 一.定义 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换,更详细一点就是封装可交换的行为,并使用委...
    小巨人Vea阅读 434评论 0 0
  • 在程序设计中,我们也常常遇到类似的情况,要实现某一个功能有多种方案可以选择。比如一个压缩文件的程序,既可以选择zi...
    yufawu阅读 369评论 0 3