toBeTheLight.github.io 荒原

阅读:《图解设计模式》4 分开考虑


书中将 Bridge 桥接模式、Strategy 策略模式归纳为用来分解易变得杂乱无章的处理的设计模式。

  • 桥接模式:将类的功能和实现分离,功能接口通过组合关系使用实现类来完成具体实现。
  • 策略模式:将过程拆分为实现和使用,从而使实现可替换,即策略可替换。

Bridge 桥接模式

定义

桥接模式是将类的功能层级结构与它的实现层次结构分离,使它们都可以独立地变化。

  • 功能层级结构:对接口的扩展(方法的扩展)。
  • 实现层级结构:对接口的实现(在这种模式下一般实现时不进行扩展)。

角色

  • 功能化:即功能层级结构的最顶级的类(书中叫抽象化,但此抽象化的“抽象”和抽象类的“抽象”没有关系,一般不含有抽象方法)。
  • 改善后的功能化:扩展了功能化的方法(同样书中叫改善后的抽象化)。
  • 实现者:即实现层级结构的最顶级的类,也正是由此,其为抽象类。
  • 具体实现者:对实现者类方法的实现。

JavaScript 实现

例子不太好想。借用书中的 Java 例子。

// 功能化类:
class Display {
  constructor (impl) {
    this.impl = impl
  }
  open () {
    this.impl.rawOpen()
  }
  print () {
    this.impl.rawPrint()
  }
  close () {
    this.impl.rawClose()
  }
  display () {
    this.open()
    this.print()
    this.close()
  }
}
// 改善后的功能化,扩展了 multiDisplay 方法
class CountDisplay extends Display {
  constructor (impl) {
    super(impl)
  }
  multiDisplay (times) {
    this.open()
    for (let i = 0; i < times; i++) {
      this.print()
    }
    this.close()
  }
}
// 还可以有其他扩展
//...

// 实现者,可以看出,位于实现结构最顶层的实现者类才是抽象类
class DisplayImpl {
  rawOpen () {
    throw new Error('需实现 DisplayImpl 类的 rawOpen 方法')
  }
  rawPrint () {
    throw new Error('需实现 DisplayImpl 类的 rawPrint 方法')
  }
  rawClose () {
    throw new Error('需实现 DisplayImpl 类的 rawClose 方法')
  }
}
// 具体实现者
class StringDisplayImpl extends DisplayImpl {
  constructor (text) {
    super()
    this.text = text
  }
  rawOpen () {
    console.log('----------')
  }
  rawPrint () {
    console.log(this.text)
  }
  rawClose () {
    console.log('----------')
  }
}
// 还可以有其他实现
//...

// 使用者
const impl = new StringDisplayImpl('Hello, Bridge')
const countDisplay = new CountDisplay(impl)
countDisplay.multiDisplay(3)

Strategy 策略模式

定义

定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。

角色

  • 抽象策略类:定义了具体策略类应实现的方法
  • 具体策略类:实现了抽象策略类的方法
  • 上下文:持有一个策略类的引用,可以对策略的使用做一些额外处理满足需求,最终给使用者调用。

JavaScript 实现

// 抽象策略类
class Recommendation {
  constructor () {
    this.data = {}
  }
  watch () {
    throw new Error('需实现 Recommendation 类的 watch 方法')
  }
  recommend () {
    throw new Error('需实现 Recommendation 类的 recommend 方法')
  }
}
// 具体策略类
class Ranking {
  constructor () {
    super()
    this.watched = []
  }
  watch (item) {
    this.watched.push(item)
  }
  recommend () {
    request({
      url: `http://demo.casdasd-recommendation.com/ranking`,
      data: this.watched
    })
  }
}

class Behavior {
  constructor () {
    super()
  }
  watch (item) {
    request({
      method: 'post',
      url: 'http://demo.casdasd-recommendation.com/behavior',
      data: item
    })
  }
  recommend () {
    request({
      url: 'http://demo.casdasd-recommendation.com/behavior'
    })
  }
}
// context
class Page {
  constructor (recommendation) {
    this.recommendation = recommendation
  }
  recommend () {
    this.recommendation.recommend()
  }
  watch (item) {
    // 这个判断代表具体业务的处理
    if (stayTime(item) > 5) {
      this.recommendation.watch(item)
    }
  }
}
// 使用方
let page
if (user.isLogin()) {
  page = const new Page(new Behavior())
} else {
  page = const new Page(new Ranking())
}
page.recommend()
// 如果用户浏览了其他的东西,还可以调用 watch,使策略进化
page.watch(item)

思考

  • 模式对比
    • 建造者模式:
      • 按照最初的分类建造者模式被归为创建实例型的设计模式,而策略模式被归为行为型模式,而我认为这只不过是一种是从创建实例的代码中归纳出来的,另一种是从过程式的代码中归纳出来的,只是一种思路在两种情况下的应用。
      • 从更高层面来说,与建造者模式相同,都是使用组合方式,让另一个类持有引用,而达到可替换的目的。
      • 实在要说不同的话,也许建造者模式只需要最终对外暴露构建实例的方法,而策略模式可能需要从环境中获取一些参数从而对策略的执行产生影响,所以需要暴露更多的方法,但归根到底,还是最终目的(创建实例并不需要复杂的 api)引起的不同,抽象上来讲还是没有本质区别的。

Content