书中将 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()