JavaScript设计模式之策略模式

在编写程序时常常会遇到这样的情况:如果条件A成立,采用处理方式A,如果条件B成立,则采用处理方式B...而这几种处理方式是类似的。策略模式就是针对这种问题提出的。

策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

一个非常经典的例子:计算员工工资

如果员工的绩效达到S级标准,1.6倍基础工资;A级,1.4倍基础工资;B级1.2倍基础工资;C级,基础工资。

最容易想到的解决方式:if else堆砌式代码

function getSalary(grade, base) {
    if (grade === 'S') {
        return base * 1.6;
    }
    if (grade === 'A') {
        return base * 1.4;
    }
    if (grade === 'B') {
        return base * 1.2;
    }
    if (grade === 'C') {
        return base;
    }
}
复制代码

这样看似简单,其实不然。如果企业迈入了一个更为繁荣的阶段,修改达到绩效标准的工资,或者需要再添加绩效标准,就需要大幅度的改动;而且采用这种设计,存在大量分支,导致代码非常臃肿。这样的设计不利于修改和维护

使用策略模式解决上述问题

策略模式需要封装一系列可互相替换的算法,在上述问题中,根据绩效计算工资的算法是可以相互替换的

首先定义根据绩效计算工资的函数

let performanceS = function() {}
performanceS.prototype.getSalary = function(base) {
    return base * 1.6;
}
let performanceA = function() {}
performanceA.prototype.getSalary = function(base) {
    return base * 1.4;
}
let performanceB = function() {}
performanceB.prototype.getSalary = function(base) {
    return base * 1.2;
}
let performanceC = function() {}
performanceC.prototype.getSalary = function(base) {
    return base;
}
复制代码

然后定义工资类,使得能够根据绩效来选择计算工资的方法

let Salary = function() {
    this.base = null;
    this.strategy = null;
}
Salary.prototype.setBase = function(salary) {
    this.salary = salary;
}
Salary.prototype.setStrategy = functon(strategy) {
    this.strategy = strategy;
}
Salary.prototype.getSalary = function() {
    return this.strategy.setSalary(this.base);
}
复制代码

调用上面编写的计算工资的方法

// 假设某个企业的销售人员的基础工资是6000元,其绩效达到S级标准
let salary = new Salary();
salary.setBase(6000);
salary.setStrategy(new performanceS());
console.log(salary.getSalary());
复制代码

以上设计的主要思想是根据上下文来将事件交给其他类来处理,如果需要增加某个绩效标准,只需要增加一个新的performance类,其他的代码不需要改动。

策略模式在前端中的应用举例:表单验证

// form_check.js
let 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;
    }
  }
};
// 校验类
function Validator() {
  this.cache = [];  // 存储校验规则
}
// 添加校验规则
Validator.prototype.add = function(dom, rules) {
  let self = this;
  for (let i = 0, rule; rule = rules[i++];) {
    (function() {
      let strategyAry = rule.strategy.split(':');  // 针对长度验证的操作
      let errorMsg = rule.errorMsg;
      self.cache.push(function() {
        let strategy = strategyAry.shift();
        strategyAry.unshift(dom.value);
        strategyAry.push(errorMsg);
        return strategies[strategy].apply(dom, strategyAry);
      });
    })(rule);
  }
};
// 验证是否满足所有校验规则
Validator.prototype.start = function() {
  for (let i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
    let msg = validatorFunc();
    if (msg) {
      return msg;
    }
  }
};
复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>#title</title>
</head>
<body>
  <form action="" id='registerForm' method="post">
    <input type="text" placeholder="请输入用户名" name='username'/>
    <input type="password" placeholder="请输入密码" name='password'/>
    <input type="text" placeholder="请输入手机号" name='phonenum'>
    <button>submit</button>
  </form>
</body>
<script src="form_check.js"></script>
<script src="index.js"></script>
</html>
复制代码
// index.js
let registerForm = document.getElementById('registerForm');
let validataFunc = function () {
  let validator = new Validator();
  validator.add(registerForm.username, [
    {
      strategy: 'isNonEmpty',
      errorMsg: 'username should not be empty!'
    },
    {
      strategy: 'minLength:6',
      errorMsg: 'username\'s length should be more than 10!'
    }
  ]);
  validator.add(registerForm.password, [
    {
      strategy: 'minLength:6',
      errorMsg: 'password\'s length should be more than 10!'
    }
  ]);
  validator.add(registerForm.phonenum, [
    {
      strategy: 'isMobile',
      errorMsg: 'wrong format phone num!'
    }
  ]);
  let errorMsg = validator.start();
  return errorMsg;
};
registerForm.onsubmit = function() {
  let errorMsg = validataFunc();
  if (errorMsg) {
    alert(errorMsg);
    return false;
  }
  return true;
};
复制代码

策略模式的优势在于满足了开闭原则,避免了if else的堆砌式的代码;其缺点是需要构建许多策略类,但是这比堆砌式的if else要好很多;如果要调用某种策略,必须知道所有的策略的运行方式,相当于暴露了其实现方式。

转载于:https://juejin.im/post/5d05f700e51d454f6f16ebd1

猜你喜欢

转载自blog.csdn.net/weixin_33716941/article/details/93181207
今日推荐