js 设计模式——策略模式

策略模式(Strategy)

定义:将定义的一组算法封装起来,使其相互之间可以替换。封装的算法具有一定的独立性,不会随客户端的变化而变化

废话不多说,先来个例子

// 例如要写一个计算两个数加减乘除的方法,会有人这么写
countNum(type, num1, num2) {
  if(type === 'add'){
    return num1 + num2
  } else if(type === 'subtract'){
    return num1 - num2
  } else if(type === 'multiply'){
    return num1 * num2
  } else {
    return num1 / num2
  }
}

countNum('add', 9, 3)       // 12
countNum('subtract', 9, 3)  // 6
countNum('multiply', 9, 3)  // 27
countNum('', 9, 3)          // 3

上面的例子,一定会有人这么写的,你要说没有我也没办法(~ ̄▽ ̄)~,这样写首先不利于维护,其实也不好方便扩展

改进一下:

let countNum = {
  add(num1, num2) {
    return num1 + num2
  },
  subtract(num1, num2) {
    return num1 - num2
  },
  multiply(num1, num2) {
    return num1 * num2
  },
  divide(num1, num2) {
    return num1 / num2
  },
}

countNum.add(9, 3)      // 12
countNum.subtract(9, 3) // 6
countNum.multiply(9, 3) // 27
countNum.divide(9, 3)   // 3

上面的例子就可以看成是一个策略模式

now,现在有个需求,快要圣诞节,所以咧,超市中一部分商品8折出售,一部分9折出售,等到元旦还要搞大事,普通用户满100返30,VIP用户满100返50,用策略模式来实现

// 定义一个策略对象
let priceceStrategy = function(){
  // 内部算法对象 
  let strategy = {
    return30(price){
      return price + parseInt(price / 100) * 30
    },
    return50(price){
      return price + parseInt(price / 100) * 50
    },
    price80(price){
      return price  * 80 / 100
    },
    price90(price){
      return price  * 90 / 100
    }
  }
  // 策略方法调用接口
  return {
    strategyFunction(type, price) {
      return strategy[type] && strategy[type](price)
    },
    // 添加算法
    addStrategy(type, fn){
      strategy[type] = fn
    }
  }
}()

priceceStrategy.strategyFunction('return30', 100)   // 130
priceceStrategy.strategyFunction('return50', 100)   // 150
priceceStrategy.strategyFunction('price80', 100)    // 80
priceceStrategy.strategyFunction('price90', 100)    // 90
// 添加一个算法
priceceStrategy.addStrategy('return70', (price) => {
  return price + parseInt(price / 100) * 70
})
console.log(priceceStrategy.strategyFunction('return70', 100))  // 170

还有我们的表单验证也可以使用策略模式

// 定义一个策略对象
let inputStrategy = function(){
  // 内部算法对象 
  let strategy = {
    notNull(value){
      return /\s+/.test(value) ? '请输入内容' : ''
    },
    number(value){
      return /^[0-9]+(\.[0-9]+)?$/.test(value) ? '' : '请输入数字'
    }
  }
  // 策略方法调用接口
  return {
    check(type, value) {
      // 去除空白符
      value = value.replace(/^\s+|\s+$/g, '')
      return strategy[type] && strategy[type](value)
    },
    // 添加策略
    addStrategy(type, fn){
      strategy[type] = fn
    }
  }
}()

// 添加算法
inputStrategy.addStrategy('nickName', (value) => {
  return /^[a-zA-Z]\w{3,7}$/.test(value) ? '' : '请输入4-8为昵称'    
})

总结

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

策略模式中的三个对象:

  • 环境对象:该类中实现了对抽象策略中定义的接口或者抽象类的引用。
  • 抽象策略对象:它可由接口或抽象类来实现。
  • 具体策略对象:它封装了实现同不功能的不同算法。

利用策略模式构建应用程序,可以根据用户配置等内容,选择不同有算法来实现应用程序的功能。具体的选择有环境对象来完成。采用这种方式可以避免由于使用条件语句而带来的代码混乱,提高应用程序的灵活性与条理性。

优点:
  1. 优化多重条件判断,采用策略模式是的算法更利于维护
  2. 可扩展性,策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  3. 策略类之间可以自由切换,由于策略类都实现同一个接口,所以使它们之间可以自由切换。
缺点:
  1. 由于选择哪种算法的决定权在用户,所以对用户来说就必须了解每种算法的实现。
  2. 由于每种算法间相互独立,这样对于一些复杂的算法处理相同逻辑的部分无法实现共享,这就会造成一些资源的浪费。
posted @ 2019-06-20 11:53  妖色调  阅读(1937)  评论(0编辑  收藏  举报