关于 flyweight-pattern
一项介绍 JavaScript 享元模式的学习技能,通过复用共享对象状态,减少大量相似对象的内存占用。示例使用 ISBN 缓存书籍数据,并区分共享信息与副本信息。
主要功能
- 01复用共享对象实例
- 02使用 Map 缓存对象
- 03分离共享状态与独有状态
- 04跟踪单本书籍副本
适用场景
- 01表示同一本书的多个副本
- 02减少相似对象的内存占用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ patternsdev/ skills --skill flyweight-pattern安装后在 Claude Code 中输入/flyweight-pattern确认已加载。
详细介绍
享元模式
创建大量相似对象时,享元模式有助于节省内存。
在我们的应用中,我们希望用户能够添加书籍。所有书籍都有 title、author 和 isbn 编号!不过,图书馆通常不会只有一本书的一个副本:同一本书通常会有多个副本。
适用场景
- 创建可能耗尽可用内存的大量对象时
- 许多对象共享相同的内在属性时(例如,ISBN 相同的书籍)
不适用场景
- 对象数量较少,内存不是问题时
- 对象几乎没有共享的内在属性时——区分内在状态和外在状态会增加复杂性,却无法节省内存
- 额外的查找和管理开销超过内存收益时
说明
- 将内在(共享)状态与外在(唯一)状态分离
- 使用 Map 或类似结构缓存并重用共享对象实例
- 在许多情况下,可以考虑使用 JavaScript 的原型继承作为更简单的替代方案
详情
如果同一本书有多个副本,那么每次都创建一个新的书籍实例并没有太大用处。相反,我们希望创建多个 Book 构造函数实例,让它们代表同一本书。
class Book {
constructor(title, author, isbn) {
this.title = title;
this.author = author;
this.isbn = isbn;
}
}
现在来实现向列表中添加新书的功能。如果一本书的 ISBN 编号相同,也就是说属于同一种书,我们就不想创建全新的 Book 实例。相反,我们应该先检查这本书是否已经存在。
const books = new Map();
const createBook = (title, author, isbn) => {
const existingBook = books.has(isbn);
if (existingBook) {
return books.get(isbn);
}
};
如果其中还没有这本书的 ISBN 编号,我们就创建一本新书,并将其 ISBN 编号添加到 isbnNumbers 集合中。
const createBook = (title, author, isbn) => {
const existingBook = books.has(isbn);
if (existingBook) {
return books.get(isbn);
}
const book = new Book(title, author, isbn);
books.set(isbn, book);
return book;
};
createBook 函数帮助我们创建同一种书的新实例。不过,图书馆通常会有同一本书的多个副本!现在来创建一个 addBook 函数,以便添加同一本书的多个副本。该函数应调用 createBook 函数,后者会返回新创建的 Book 实例,或返回已经存在的实例。
为了跟踪副本总数,我们创建一个 bookList 数组,用于存放图书馆中的所有书籍。
const bookList = [];
const addBook = (title, author, isbn, availability, sales) => {
const book = {
...createBook(title, author, isbn),
sales,
availability,
isbn,
};
bookList.push(book);
return book;
};
太好了!每次添加一本书的副本时,我们都可以有效地重用该书已有的 Book 实例,而不是创建新的实例。现在为 3 本书创建 5 个副本:Harry Potter、To Kill a Mockingbird 和 The Great Gatsby。
addBook("Harry Potter", "JK Rowling", "AB123", false, 100);
addBook("Harry Potter", "JK Rowling", "AB123", true, 50);
addBook("To Kill a Mockingbird", "Harper Lee", "CD345", true, 10);
addBook("To Kill a Mockingbird", "Harper Lee", "CD345", false, 20);
addBook("The Great Gatsby", "F. Scott Fitzgerald", "EF567", false, 20);
虽然有 5 个副本,但我们只有 3 个 Book 实例!
创建大量可能耗尽所有可用 RAM 的对象时,享元模式很有用。它可以帮助我们尽量减少内存消耗。
在 JavaScript 中,我们可以通过原型继承轻松解决这个问题。如今,硬件通常配有数 GB 的 RAM,因此享元模式的重要性有所降低。
来源
参考资料
- Flyweight - Refactoring Guru
- Flyweight Design Pattern - How To Do In Java