Flyweight 亨元模式
1、亨元模式
享元模式是一个优化重复、缓慢和低效数据共享代码的经典结构化解决方案。它的目标是以相关对象尽可能多的共享数据,来减少应用程序中内存的使用(例如:应用程序的配置、状态等)。
2、亨元模式使用方法
a) 数据层
数据层,基于存储在内存中的大量相同对象的数据共享的概念。亨元模式大部分时候被应用于数据层。
亨元模式的实例:
实现一个管理一个图书馆中所有书籍的系统。
每一本书的重要元数据:
- ID
- 标题
- 作者
- 类型
- 总页数
- 出版商ID
- ISBN
将需要下面一些属性,来跟踪哪一个成员是被借出的一本特定的书,借出它们的日期,还有预计的归还日期。
- 借出日期
- 借出的成员
- 规定归还时间
- 可借性
var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){ this.id = id; this.title = title; this.author = author; this.genre = genre; this.pageCount = pageCount; this.publisherID = publisherID; this.ISBN = ISBN; this.checkoutDate = checkoutDate; this.checkoutMember = checkoutMember; this.dueReturnDate = dueReturnDate; this.availability = availability; }; Book.prototype = { getTitle: function () { return this.title; }, getAuthor: function () { return this.author; }, getISBN: function (){ return this.ISBN; }, // 更新是否借出的状态 updateCheckoutStatus: function( bookID, newStatus, checkoutDate , checkoutMember, newReturnDate ){ this.id = bookID; this.availability = newStatus; this.checkoutDate = checkoutDate; this.checkoutMember = checkoutMember; this.dueReturnDate = newReturnDate; }, // 延长借出期限 extendCheckoutPeriod: function( bookID, newReturnDate ){ this.id = bookID; this.dueReturnDate = newReturnDate; }, // 是否逾期,返回true/false isPastDue: function(bookID){ var currentDate = new Date(); return currentDate.getTime() > Date.parse( this.dueReturnDate ); } };
以上实例对于小规模的藏书可能工作得还好,然而当图书馆扩充至每一本书的多个版本和可用的备份,这样一个大型的库存,我们会发现管理系统的运行随着时间的推移会越来越慢。使用成千上万的书籍对象可能会压倒内存,而我们可以通过享元模式的提升来优化我们的系统:
下面的书籍元数据组合的单一实体将在所有带有一个特定标题的书籍拷贝中共享:
// 亨元模式优化版本 var Book = function ( 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 = { }, existingBook; return { createBook: function ( title, author, genre, pageCount, publisherID, ISBN ) { // 检查以前是否创建过特定的图书元数据组合 // 或者强制返回一个布尔值 existingBook = existingBooks[ISBN]; if ( !!existingBook ) { // 如果存在,直接返回 return existingBook; } else { // 如果没有,让我们创建图书的新实例并存储它 var book = new Book( title, author, genre, pageCount, publisherID, ISBN ); existingBooks[ISBN] = book; return book; } } }; });
将那些从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 }; }, // 更新借出的状态 updateCheckoutStatus: function ( bookID, newStatus, checkoutDate, checkoutMember, newReturnDate ) { var record = bookRecordDatabase[bookID]; record.availability = newStatus; record.checkoutDate = checkoutDate; record.checkoutMember = checkoutMember; record.dueReturnDate = newReturnDate; }, // 延长借出期限 extendCheckoutPeriod: function ( bookID, newReturnDate ) { bookRecordDatabase[bookID].dueReturnDate = newReturnDate; }, // 是否逾期 isPastDue: function ( bookID ) { var currentDate = new Date(); return currentDate.getTime() > Date.parse( bookRecordDatabase[bookID].dueReturnDate ); } }; });
这些改变的结果是所有从Book类中提取的数据,现在被存储到了BookManager单例(BookDatabase)的一个属性之中——与我们以前使用大量对象相比可以被认为是更加高效的东西。同书籍借出相关的方法也被设置在这里,因为它们处理的数据是外在的而不内在的。
这个过程确实给我们最终的解决方法增加了一点点复杂性,然而同已经明智解决的数据性能问题相比,这只是一个小担忧,如果我们有同一本书的30份拷贝,现在我们只需要存储它一次就够了。每一个函数也会占用内存。使用享元模式这些函数只在一个地方存在(就是在管理器上),并且不是在每一个对象上面,这节约了内存上的使用。
b) DOM层
DOM层,享元模式被作为事件管理中心,以避免将事件处理程序关联到我们需要相同行为父容器的所有子节点上。