# 享元模式
# 参考
http://c.biancheng.net/view/1371.html (opens new window)
深入理解JavaScript系列(37):设计模式之享元模式 (opens new window)
# 定义
运用共享技术来有効地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
# 应用场景
如:享元模式在编辑器软件中大量使用,如在一个文档中多次出现相同的图片,则只需要创建一个图片对象,通过在应用程序中设置该图片出现的位置,可以实现该图片在不同地方多次重复显示。
# 适用场景
一个程序中使用了大量的相似对象,造成了很大的内存开销。
大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
# 实现、应用思路
享元模式中存在以下两种状态:
内部状态,即不会随着环境的改变而改变的可共享部分。
外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。
享元模式模式的结构组成,主要角色有如下:
抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口, 非享元的外部状态以参数的形式通过方法传入。(在JavaScript中很多时候, 抽象享元角色可能并不太必要,这时候可以将抽象享元角色和具体享元角色合并为一个享元角色Flyweight)
具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
# 优点
享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
# 缺点
享元模式使得系统更加复杂,需要分离出内部状态和外部状态,以及创造管理享元对象、外部状态的函数,这使得程序的逻辑复杂化。
为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。(时间换空间)
# 代码实例
/* 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))