toBeTheLight.github.io 荒原

阅读:《图解设计模式》9 避免浪费


书中将 Flyweight 享元模式、Proxy 代理模式归纳为状态相关的设计模式。 享元模式:将相似的部分提取出来,通过共享此实例来避免 new 出实例,节省系统开支。 代理模式:给某对象提供一个代理以控制对该对象的访问,可将消耗大量资源的操作控制在必要时进行。

Flyweight 享元模式

定义

通过尽量共享实例来避免 new 出实例。

角色

  • 享元:实例会被共享的类。
  • 享元工厂:生成享元和管理享元。

JavaScript 实现

// 享元
class City {
  constructor (name) {
    const { number, employ } = fs.readFileSync(`./cities/${name}.json`)
    this.number = number
    this.employ = employ
  }
}
// 享元工厂
class CityFactory {
  constructor () {
    this.pool = {}
  }
  get(name) {
    if (this.pool[name]) return this.pool[name]
    const city = new City(name)
    this.pool[name] = city
    return city
  }
}
// 使用者
const factory = new CityFactory()
const beijing = factory.get('北京')
const shanghai = factory.get('上海')

思考

享元模式可以说是单例模式在另一个层次的体现,可以以内容为界定界限,如果没有内容的区别,可以使用单例模式。

拓展

由于享元模式是以尽量共享相同部分来节省系统资源开支的,那么不同不能共享的部分外部话,可以使用参数的形式传入到享元中,也可以使用组合的形式,与享元对象组合成新的对象。

Proxy 代理模式

定义

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。

角色

  • 抽象主体:定义了实际主体和代理人需要实现的接口,从而使代理人和实际主体 api 一致。
  • 实际主体:同样实现了抽象主体的接口。
  • 代理人:实现了抽象主体的接口。

JavaScript 实现

我们假设主体的实例化非常消耗资源,使用代理模式使实例化的过程在必要时进行。

// 抽象主体
class Subject {
  constructor ({ name } = {}) {
    this.name = name
  }
  setName (name) {
    throw new Error('需实现 Subject 类的 setName 方法')
  }
  getName () {
    throw new Error('需实现 Subject 类的 getName 方法')
  }
  print () {
    throw new Error('需实现 Subject 类的 getName 方法')
  }
}
// 实际主体
class Printer extends Subject {
  constructor ({ name } = {}) {
    super()
    console.log('实例化老费劲了')
    this.name = name
  }
  setName (name) {
    this.name = name
  }
  getName () {
    return this.name
  }
  print () {
    console.log(`***${this.name}***`)
  }
}
// 代理人
const REALIZE = Symbol('realize')
class PrinterProxy extends Subject {
  constructor ({ name } = {}) {
    super()
    this.name = name
    this.real = null
  }
  setName (name) {
    this.name = name
  }
  getName () {
    return this.name
  }
  print () {
    // 通过代理的方式,在必要的时候才会去进行
    this[REALIZE]()
    this.real.print()
  }
  [REALIZE] () {
    if (this.real) return
    this.real = new Printer({ name: this.name })
  }
}
// 使用者
const printer = new PrinterProxy()
printer.setName('打印机')
console.log(printer.getName())
printer.print()

Content