设计模式系列笔记-策略模式

写在前面:本系列文章内容为《JavaScript设计模式与开发实践》一书学习笔记,感谢作者曾探

策略模式

在现实中,很多时候可以选择多种途径到达同一个目的地,程序设计中,要实现某一个功能有多种方案可以选择,可以根据具体情况选择其中一种方案,这就是策略模式
定义:策略模式就是定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换

1.策略模式实现

举例:年终奖的计算(绩效为 S 的人年
终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资)
一般最容易想到的办法就是使用组合函数,代码如下:

const performanceS = (salary) => {
  return salary * 4;
};
const performanceA = (salary) => {
  return salary * 3;
};
const performanceB = (salary) => {
  return salary * 2;
};
const calculateBonus = (performanceLevel, salary) => {
  if (performanceLevel === 'S') {
    return performanceS(salary);
  }
  if (performanceLevel === 'A') {
    return performanceA(salary);
  }
  if (performanceLevel === 'B') {
    return performanceB(salary);
  }
};
calculateBonus('A', 10000); // 输出:30000

缺陷:
calculateBonus 函数有可能越来越庞大,而且在系统变化的时候缺乏弹性

2.JavaScript 的策略模式实现

// 策略模式
const strategies = {
  "S": (salary) => {
    return salary * 4;
  },
  "A": (salary) => {
    return salary * 3;
  },
  "B": (salary) => {
    return salary * 2;
  }
};
const calculateBonus = (level, salary) => {
  return strategies[level](salary);
};
console.log(calculateBonus('S', 20000)); // 输出:80000
console.log(calculateBonus('A', 10000)); // 输出:30000

3.策略模式实现表单校验

假设我们正在编写一个注册的页面,在点击注册按钮之前,有如下几条校验逻辑

  • 用户名不能为空
  • 密码长度不能少于 6 位
  • 手机号码必须符合格式

HTML部分

<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>
  1. 我们先把校验逻辑都封装成策略对象
var strategies = {
  isNonEmpty: function(value, errorMsg) { // 不为空
    if (value === '') {
      return errorMsg;
    }
  },
  minLength: function(value, length, errorMsg) { // 限制最小长度
    if (value.length < length) {
      return errorMsg;
    }
  },
  isMobile: function(value, errorMsg) { // 手机号码格式
    if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg;
    }
  }
};
  1. 接下来我们期望的使用方法
var validataFunc = function() {
  var validator = new Validator(); // 创建一个 validator 对象
  /***************添加一些校验规则****************/
  validator.add(registerForm.userName, 'isNonEmpty', '用户名不能为空');
  validator.add(registerForm.password, 'minLength:6', '密码长度不能少于 6 位');
  validator.add(registerForm.phoneNumber, 'isMobile', '手机号码格式不正确');
  var errorMsg = validator.start(); // 获得校验结果
  return errorMsg; // 返回校验结果
}
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function() {
  var errorMsg = validataFunc(); // 如果 errorMsg 有确切的返回值,说明未通过校验
  if (errorMsg) {
    alert(errorMsg);
    return false; // 阻止表单提交
  }
};
  1. 实现我们需要的 Validator 类
var Validator = function() {
  this.cache = []; // 保存校验规则
};
Validator.prototype.add = function(dom, rule, errorMsg) {
  var ary = rule.split(':'); // 把 strategy 和参数分开
  this.cache.push(function() { // 把校验的步骤用空函数包装起来,并且放入 cache
    var strategy = ary.shift(); // 用户挑选的 strategy
    ary.unshift(dom.value); // 把 input 的 value 添加进参数列表
    ary.push(errorMsg); // 把 errorMsg 添加进参数列表
    return strategies[strategy].apply(dom, ary);
  });
};
Validator.prototype.start = function() {
  for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
    var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息
    if (msg) { // 如果有确切的返回值,说明校验没有通过
      return msg;
    }
  }
};

使用策略模式重构代码之后,我们仅仅通过“配置”的方式就可以完成一个表单的校验,这些校验规则也可以复用在程序的任何地方,还能作为插件的形式,方便地被移植到其他项目中

4.策略模式优缺点

优点:

  • 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句
  • 策略模式提供了对开放—封闭原则的完美支持,易于切换,易于理解,易于扩展

缺点:

  • 会在程序中增加许多策略类或者策略对象
  • 要使用策略模式,必须了解所有的 strategy,必须了解各个 strategy 之间的不同点,这样才能选择一个合适的 strategy,一定程度上违背了最少知识原则

5.一等函数对象与策略模式

在函数作为一等对象的语言中,策略模式是隐形的
在 JavaScript 中,除了使用类来封装算法和行为之外,使用函数当然也是一种选择。这些“算法”可以被封装到函数中并且四处传递,也就是我们常说的"高阶函数”

在 JavaScript 这种将函数作为一等对象的语言里,策略模式已经融入到了语言本身当中,我们经常用高阶函数来封装不同的行为,并且把它传递到另一个函数中,例如:

var S = function(salary) {
  return salary * 4;
};
var A = function(salary) {
  return salary * 3;
};
var B = function(salary) {
  return salary * 2;
};
var calculateBonus = function(func, salary) {
  return func(salary);
};
calculateBonus(S, 10000); // 输出:40000

在 JavaScript 语言的策略模式中,策略类往往被函数所代替,这时策略模式就成为一种“隐形”的模式

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

推荐阅读更多精彩内容

  • javascript设计模式与开发实践 设计模式 每个设计模式我们需要从三点问题入手: 定义 作用 用法与实现 单...
    穿牛仔裤的蚊子阅读 4,047评论 0 13
  • 在程序设计中,我们也常常遇到类似的情况,要实现某一个功能有多种方案可以选择。比如一个压缩文件的程序,既可以选择zi...
    yufawu阅读 367评论 0 3
  • 工厂模式 单体模式 模块模式 代理模式 职责链模式 命令模式 模板方法模式 策略模式 发布-订阅模式 中介者模式 ...
    HelloJames阅读 1,008评论 0 6
  • 摘自《JavaScript设计模式与开发实践》 在现实中,很多时候有多种途径到达同一个目的地。比如我们要去某个地方...
    小小的白菜阅读 283评论 0 0
  • 转载:https://www.cnblogs.com/tugenhua0707/p/4722696.html理解j...
    Picidae阅读 302评论 0 1