策略模式:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换(相同的目标和意图)

将不变的部分和可变的部分分隔开

问题代码

计算绩效 = 员工的工资数 * 对应的绩效

未重构前的写法,存在问题

  • 包含比较多的 if条件语句
  • calculateBonus 函数缺乏弹性,如果需要增加新的绩效模式,又要修改内部实现
  • 算法复用性
  1. // 最初写法
  2. var calculateBonus = function (performanceLevel, salary) {
  3. if (performanceLevel === 'S') return salary * 4
  4. if (performanceLevel === 'A') return salary * 3
  5. if (performanceLevel === 'B') return salary * 2
  6. }

传统实现

**

  1. // 定义一系列的算法,各自封装成策略类,算法被封装在策略类内部的方法
  2. var performanceS = function () { }
  3. performanceS.prototype.calculate = function (salary) {
  4. return salary * 4
  5. }
  6. var performanceA = function () { }
  7. performanceA.prototype.calculate = function (salary) {
  8. return salary * 3
  9. }
  10. var performanceB = function () { }
  11. performanceB.prototype.calculate = function (salary) {
  12. return salary * 2
  13. }
  14. var Bonus = function () {
  15. this.salary = null
  16. this.strategy = null
  17. }
  18. Bonus.prototype.setSalary = function (salary) {
  19. this.salary = salary
  20. }
  21. Bonus.prototype.setStrategy = function (strategy) {
  22. this.strategy = strategy
  23. }
  24. Bonus.prototype.getBonus = function () {
  25. return this.strategy.calculate(this.salary) // 把计算奖金的操作委托给相应的策略对象
  26. }
  27. var bonus = new Bonus()
  28. bonus.setSalary(10000)
  29. bonus.setStrategy(new performanceS())

JavaScript的策略模式

  1. var strategies = {
  2. "S": function (salary) {
  3. return salary * 4
  4. },
  5. "A": function (salary) {
  6. return salary * 3
  7. },
  8. "B": function (salary) {
  9. return salary * 2
  10. }
  11. }
  12. var calculateBonus = function (level, salary) {
  13. return strategies[level](salary)
  14. }

校验表单

表单的校验逻辑有:

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

常见模式

  1. /**
  2. * 常见的使用条件判断方法
  3. */
  4. var registerForm = document.getElementById('register')
  5. registerForm.onsubmit = function () {
  6. if (registerForm.username.value === '') {
  7. console.log('用于名不能空')
  8. return false
  9. }
  10. if (registerForm.password.value.length < 6) {
  11. console.log('密码长度不能少于6')
  12. return false
  13. }
  14. if (!/(^1[3|5|8|7|4][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
  15. console.log('手机号码格式不正确')
  16. return false
  17. }
  18. }

使用策略模式:

  1. // 提取校验逻辑算法,封装为策略对象
  2. const strategies = {
  3. isNonEmpty(value, errorMsg) {
  4. if (value === '') {
  5. return errorMsg
  6. }
  7. },
  8. minLength(value, length, errorMsg) {
  9. if (value.length < length) {
  10. return errorMsg
  11. }
  12. },
  13. isMobile(value, errorMsg) {
  14. if (!/(^1[3|5|8|7|4][0-9]{9}$)/.test(value)) {
  15. return errorMsg
  16. }
  17. }
  18. }
  19. // Validator类作为Context,负责接收用户的请求并委托给策略对象进行处理
  20. const Validator = function () {
  21. this.cache = [] //保存校验规则
  22. }
  23. Validator.prototype.add = function (dom, rule, errorMsg) {
  24. var ary = rule.split(':') // 把stragy和参数分开
  25. this.cache.push(function () {
  26. var strategy = ary.shift()
  27. ary.unshift(dom.value)
  28. ary.push(errorMsg)
  29. return strategies[strategy].apply(dom, ary)
  30. })
  31. }
  32. Validator.prototype.start = function () {
  33. for (var i = 0, validatorFunc; validateFunc = this.cache[i++];) {
  34. var msg = validateFunc()
  35. if (msg) {
  36. return msg
  37. }
  38. }
  39. }
  40. // 调用
  41. const validateFunc = function () {
  42. const validator = new Validator()
  43. validator.add(registerForm.username, 'isNonEmpty', ' 用户名不能为空')
  44. validator.add(registerForm.password, 'minLength:6', '密码长度不能少于6位')
  45. validator.add(registerForm.phoneNumber, 'isMobile', '手机号码格式不正确')
  46. const errorMsg = validator.start()
  47. return errorMsg
  48. }

总结

策略模式具有的
优点:

  • 策略模式利用组合、委托和多态等技术和思想,可以有效避免多重条件选择语句
  • 提供了对开放-封闭原则的完美支持,将算法独立封装。便于理解、扩展
  • 封装的算法能复用

什么时候会用到策略模式,当出现使用重复的 if-else 等条件语句时,便可考虑使用策略模式,将 context 和算法分离。