设计模式——策略模式

176 阅读5分钟

场景:去某个地方旅游,根据实际情况选择出行的路线。

解决方案:

  • 经济充裕的话,可以选择坐飞机。
  • 经济差不多的话,可以选择坐大巴。
  • 经济不充裕的话,可以选择骑自行车。

策略模式:

  • 概念:定义一系列的算法,把它们分别单独封装起来,使他们可以相互替换。
  • 目的:将算法的使用和算法的实现分离开来。
  • 组成:一组策略类,封装了具体的算法,负责具体的计算过程。一个环境类,接收客户的请求,随后把请求委托给环境类。(关键:说明了这个环境类要持有策略类的引用,才能实现委托)

应用举例:计算年终奖

  • 年终奖:根据员工的工资基数和年底绩效情况发放。
    • 绩效S:工资*4
    • 绩效A:工资*3
    • 绩效B:工资*2
  • 分析:找出不变的部分和可变的部分。
    • (1)不管是绩效S,绩效A,还是绩效B也好,员工都有对应的绩效,因此从一个大的范围,泛谈的话,员工都有绩效。
    • (2)不管是工资高还是工资低,员工都有自己的工资。
    • (3)根据第一和第二条可以知道,不管是年终奖多还是年终奖少,员工都有年终奖。因此最后得出:不变的总部分是年终奖大家都有,即大家都有年终奖=绩效*工资
    • (4)可变的部分:深入具体每个人的绩效,大家都是不同的;深入具体每个人的工资,大家都是不同的;深入具体每个人的年终奖,大家都是不同的。
  • 理解思路:提取出大家相似的部分单独隔离起来,再把大家各自具体不同的部分各自按各自的具体算法封装起来。(js当中一般就是函数的方式)

代码实现

利用if条件语句,实现年终奖的计算:

let getBonus=function(performance,salary){
  if(performance==='S'){
    return salary*4;
  }else if(performance==='A'){
    return salary*3;
  }else if(performance=='B'){
    return salary*2;
  }
}
const S_4000=getBonus('S',4000);
const S_5000=getBonus('S',5000);
const A_2000=getBonus('A',2000);
const A_3000=getBonus('A',3000);
console.log(S_4000,S_5000,A_2000,A_3000);

面向对象方式实现策略模式:

class PerformanceS{
  getBonus(salary){
    return salary*4;
  }
}
class PerformanceA{
  getBonus(salary){
    return salary*3;
  }
}
class PerformanceB{
  getBonus(salary){
    return salary*2;
  }
}
class Bonus{
  constructor(){
    this.salary=null;
    this.strategy=null;
  }
  setSalary(salary){
    this.salary=salary;
  }
  setStrategy(strategy){
    this.strategy=strategy;
  }
  getBonus(){
    return this.strategy.getBonus(this.salary);
  }
}
const bonus=new Bonus();
const performanceS=new PerformanceS();
const performanceA=new PerformanceA();
bonus.setSalary(4000);
bonus.setStrategy(performanceS);
let bns=bonus.getBonus();
console.log(bonus);

javascript实现策略模式:

const strategies={
  "S":function(salary){return salary*4 },
  "A":function(salary){return salary*3},
  "B":function(salary){return salary*2}
};
const getBonus=function(strategy,salary){
  return strategies[strategy](salary);
};

应用举例:表单校验

<body>
<form id="registerForm">
    <input id="username"/>
    <input id="password"/>
    <input id="phoneNumber"/>
    <input type="submit"/>
  </form>
  <script src="./表单校验.js"></script>
</body>
/**
 * 策略模式:
 * 变化部分:策略对象,表单的校验有许多不同的规则
 * 不变的部分:
 */
let strategy = {
  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;
    }
  }
}

//获取form表单
let registerForm = document.getElementById('registerForm');
/**registerForm.onsubmit <Function>
 * 任务:点击提交按钮时,启动校验函数,如果校验函数返回值errorMsg不为空,就输出这个errorMsg,同时阻止表单提交。
 */
registerForm.onsubmit = function () {
  let errorMsg = validaFunc();
  if (errorMsg) {
    alert(errorMsg);
    return false; //阻止表单提交
  }

}
/**validataFunc <Function>
 * 任务:创建一个校验器,用于添加校验规则,同时启动校验器,返回校验器校验结果errorMsg
 */
let validataFunc = function () {
  let validator = new Validator();
  validator.add(registerForm.userName, 'isNonEmpty', '用户名不能为空');
  validator.add(registerForm.password, 'minLength:6', '密码长度不能少于6位');
  validator.add(registerForm.phoneNumber, 'isMobile', '手机号码格式不正确');
  let errorMsg = validator.start();
  return errorMsg;
};
/**Validator <Constructor>
 * 角色:Context类
 * 作用:负责接收用户的请求,并委托给strategy对象。
 */
let Validator = function () {
  this.cache = []; //保存校验规则
}
/**Validator.prototype.add <Function>
 * 作用:添加规则
 * 参数:
 * dom <Object> 需要添加规则的DOM节点
 * rule <String> 需要添加的规则名称
 * errorMsg <String> 校验错误提示消息
 */
Validator.prototype.add = function (dom, rule, errorMsg) {
  let arr = rule.split(':'); //将strategy和参数分开来 'isMobile:6' 结果获得[strategy,6]
  /**validate <Function>
   *任务:收集校验参数,然后调用策略对象,并传入参数。
   */
  let validate = function () {
    let strategy = arr.shift(); //从[strategy,6]中获取strategy
    arr.unshift(dom.value); //将表单字段的值放入[6,]中,结果获的[dom.value,6]
    arr.push(errorMsg); //再将需要提示的错误消息放入数组arr中,结果获得[dom.value,6,errorMsg]
    return strategies[strategy].apply(dom, arr);
  }
  this.cache.push(validate); //将校验规则添加进数组当中
};
/**Validator.prototype.start  <Function>
 * 任务:循环校验器中的保存的校验规则,对每一个校验规则启动校验,然后如果校验不过,有返回消息,就返回。
 * 自我看法:这个函数的作用感觉只是起到一个遍历的作用,调用后遍历校验器validator中的cache属性(数组),然后
 * 对这个数组中的每一个ValidatorFunc进行调用,获取msg,不为空,就返回msg
 */
Validator.prototype.start = function () {
  for (let i = 0; ValidatorFunc; ValidatorFunc = this.cache[i++]) {
    let msg = ValidatorFunc(); //启动校验,并获取校验返回的消息
    if (msg) { //如果这个消息存在,说明肯定没有验证成功
      return msg;
    }
  }
}

作者简介:serics,芦苇科技web前端开发工程师,擅长网站建设、微信公众号开发、微信小程序开发、小游戏制作、企业微信制作、H5建设,专注于前端框架、服务端渲染、SEO技术、交互设计、图像绘制、数据分析等研究。

欢迎和我们一起并肩作战: web@talkmoney.cn 访问 www.talkmoney.cn 了解更多

提供深圳微信公众号制作,高质量的钉钉外包,广东企业微信建设,东莞微信小程序制作,专业的小游戏开发,广州H5建设