# 享元模式

# 参考

https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/flyweight.html (opens new window)

http://c.biancheng.net/view/1371.html (opens new window)

深入理解JavaScript系列(37):设计模式之享元模式 (opens new window)

# 定义

运用共享技术来有効地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

# 应用场景

如:享元模式在编辑器软件中大量使用,如在一个文档中多次出现相同的图片,则只需要创建一个图片对象,通过在应用程序中设置该图片出现的位置,可以实现该图片在不同地方多次重复显示。

# 适用场景

  • 一个程序中使用了大量的相似对象,造成了很大的内存开销。

  • 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。

  • 由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。

# 实现、应用思路

享元模式中存在以下两种状态:

  1. 内部状态,即不会随着环境的改变而改变的可共享部分。

  2. 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。

享元模式模式的结构组成,主要角色有如下:

  1. 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口, 非享元的外部状态以参数的形式通过方法传入。(在JavaScript中很多时候, 抽象享元角色可能并不太必要,这时候可以将抽象享元角色和具体享元角色合并为一个享元角色Flyweight)

  2. 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。

  3. 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。

  4. 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

# 优点

  1. 享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。

  2. 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

# 缺点

  1. 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,以及创造管理享元对象、外部状态的函数,这使得程序的逻辑复杂化。

  2. 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。(时间换空间)

# 代码实例

/* OriginBook这个类作为图书馆系统中的一个对象,如果书本少的话可能没什么问题,
但是如果书本多,每本书都要new一个对象,那么就会产生大量的内存占用,
这里可以看到书本的title和author、ISBN其实都是不变的,变化的是借阅者checkoutMember和书本id,
所以我们将不变的内部状态title和author与变化的外部状态id、checkoutMember分离,
让相同的书本都复用同一个类,这样就能达到节省内存空间的作用。 */

class OriginBook {
  id: string
  title: string
  ISBN: number
  author: string
  checkoutMember: string
  constructor(id, ISBN, title, author, checkoutMember) {
    this.id = id
    this.title = title
    this.ISBN = ISBN
    this.author = author
    this.checkoutMember = checkoutMember
  }
}

// 将OriginBook重构为更细粒度的Book享元类
class Book {
  title: string
  ISBN: number
  author: string
  constructor(ISBN, title, author) {
    this.title = title
    this.ISBN = ISBN
    this.author = author
  }
  getAuthor(): string {
    return this.author
  }
}

// 创建一个函数享元工厂来管理享元类,保证一本书只创建一个对象
const bookFactory = (() => {
  const books = {}
  return {
    create: (ISBN, title, author) => {
      let book = books[ISBN]
      if (book) return book
      book = new Book(ISBN, title, author)
      books[ISBN] = book
      return book
    }
  }
})()

// 创建一个函数来管理外部状态
const bookManager = (() => {
  const bookRecordDatabase = {}
  return {
    // 添加借书记录
    addBookRecord: (ISBN, title, author, id, checkoutMember) => {
      const book = bookFactory.create(ISBN, title, author)
      bookRecordDatabase[id] = {
        id,
        checkoutMember,
        book
      }
    },
    getBookRecord: id => bookRecordDatabase[id]
  }
})()

bookManager.addBookRecord(123, '橘子', '鲁迅', 1, '小明')
console.log(bookManager.getBookRecord(1))
上次更新: 5/17/2020, 9:24:33 AM