摘要
享元模式是用于性能优化的设计模式之一,在前端编程中有重要的应用,尤其是在大量渲染DOM的时候,使用享元模式及对象池技术能获得极大优化。本文介绍了享元模式的概念,并将其用于渲染大量列表数据的优化上。
初识享元模式
在面向对象编程中,有时会重复创建大量相似的对象,当这些对象不能被垃圾回收的时候(比如被闭包在一个回调函数中)就会造成内存的高消耗,在循环体里创建对象时尤其会出现这种情况。享元模式提出了一种对象复用的技术,即我们不需要创建那么多对象,只需要创建若干个能够被复用的对象(享元对象),然后在实际使用中给享元对象注入差异,从而使对象有不同的表现。
为了要创建享元对象,首先要把对象的数据划分为内部状态和外部状态,具体何为内部状态,何为外部状态取决于你想要创建什么样的享元对象。
举个例子:
书这个类,我想创建的享元对象是“技术类书籍”,让所有技术类的书都共享这个对象,那么书的类别就是内部状态;而书的书名,作者可能是每本书都不一样的,那么书的书名和作者就是外部状态。或者换一种方式,我想创建“村上春树写的书”这种享元对象,然后让所有村上春树写的书都共享这个享元对象,此时书的作者就为内部状态。当然也可以让作者、分类同时为内部状态创建一个享元对象。
享元对象可以按照内部状态的不同创建若干个,比如技术类书,文学类书,鸡汤类书三个。在实践的时候会发现,抽象程度越高,所创建的享元对象就越少,但是外部状态就越多;相反抽象程度越低,所需创建的享元对象就越多,外部状态就越少。特别地,当对象的所有状态都归为内部状态时,此时每个对象都可以看作一个享元对象,但是没有被共享,相当于没用享元模式。
一.享元模式的结构
1.内部状态与外部状态
在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,反之随着环境改变而改变的,不可共享的状态称之为外部状态。
简单地说,内部状态是对象本身的属性,外部状态是管理这些对象所需的额外的属性,相同的对象内部状态相同,但外部状态可能不同(比如2本相同的书被2个人借走了)
2.享元
享元是相似对象(书的例子中指的是完全相同的书,而不是题材相似的书)间可共享的属性的集合,比如书的名字、作者、ISBN等等,完全相同的书这些信息都是相同的,没有必要把相同的属性在多个相似对象中保存多份,享元负责把这些属性分离出来,以便共享
如果可共享的属性比较复杂,还可以增加抽象享元,以及与之对应的具体享元,还可以有复合享元
3.享元工厂
享元工厂负责创建并管理享元,实现共享逻辑(创建时判断是否存在,已存在就返回现有对象,否则创建一个)
4.客户端(Client)
Client负责调用享元工厂,并存储管理相似对象所需的额外属性(比如书的id,借/还日期,是否在馆等等)
实例:
如果需要管理的书籍数量非常大,那么使用享元模式节省的内存将是一个可观的数目
// 图书管理
// 书的属性:id,title,author,genre,page count,publisher id,isbn
// 管理所需的额外属性:checkout date,checkout member,due return date,availability
// 享元(存储内部状态)
function Book(title, author, genre, pageCount, publisherId, isbn) {
this.title = title;
this.author = author;
this.genre = genre;
this.pageCount = pageCount;
this.publisherId = publisherId;
this.isbn = isbn;
}
// 享元工厂(创建/管理享元)
var BookFactory = (function () {
var existingBooks = {
};
var existingBook = null;
return {
createBook: function (title, author, genre, pageCount, publisherId, isbn) {
// 如果书籍已经创建,,则找到并返回
// !!强制返回bool类型
existingBook = existingBooks[isbn];
if (!!existingBook) {
return existingBook;
} else {
// 如果不存在选择创建该书的新实例并保存
var book = new Book(title, author, genre, pageCount, publisherId, isbn);
console.log(book);
existingBooks[isbn] = book;
return book;
}
}
}
})();
// 客户端(存储外部状态)
var BookRecordManager = (function () {
var bookRecordDatabase = {
};
return {
// 添加新书到数据库
addBookRecord: function (id, title, author, genre, pageCount, publisherId, isbn,
checkoutDate, checkoutMember, dueReturnDate, availability) {
var book = BookFactory.createBook(title, author, genre, pageCount, publisherId, isbn);
bookRecordDatabase[id] = {
checkoutMember: checkoutMember,
checkoutDate: checkoutDate,
dueReturnDate: dueReturnDate,
availability: availability,
book: book
}
},
updateCheckStatus: function (bookId, newStatus, checkoutDate