1. Structure of the Flyweight Pattern
0. Intrinsic State and Extrinsic State
The shared part within the flyweight object that does not change with the environment can be called the intrinsic state of the flyweight object, conversely, the state that changes with the environment and cannot be shared is called the extrinsic state.
Simply put, intrinsic state is the object's own attributes, extrinsic state is the additional attributes needed to manage these objects, the same object has the same intrinsic state, but the extrinsic state may be different (for example, 2 identical books are borrowed by 2 people)
1. Flyweight
Flyweight is a collection of shareable attributes between similar objects (in the book example, it refers to exactly the same book, not books of similar genres), such as book title, author, ISBN, etc., for exactly the same books these information are all the same, there is no need to save the same attributes multiple times in multiple similar objects, the flyweight is responsible for separating these attributes for sharing
If the shareable attributes are more complex, you can also add abstract flyweight, and concrete flyweight corresponding to it, there can also be composite flyweight
2. Flyweight Factory
Flyweight factory is responsible for creating and managing flyweights, implementing sharing logic (judge whether it exists when creating, return existing object if it exists, otherwise create a new one)
3. Client
Client is responsible for calling the flyweight factory, and storing additional attributes needed to manage similar objects (such as book id, borrow/return date, whether it is in the library, etc.)
2. Flyweight Pattern Example
// 图书管理
// 书的属性
// 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('new 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, 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 currDate = new Date();
return currDate.getTime() > Date.parse(bookRecordDatabase[bookId].dueReturnDate);
}
};
})();
// test
// isbn 号是书籍的唯一标识,以下三条只会创建一个 book 对象
BookRecordManager.addBookRecord(1, 'x', 'x', 'xx', 300, 10001, '100-232-32'); // new book
BookRecordManager.addBookRecord(1, 'xx', 'xx', 'xx', 300, 10001, '100-232-32');
BookRecordManager.addBookRecord(1, 'xxx', 'xxx', 'xxx', 300, 10001, '100-232-32');
If the number of books that need to be managed is very large, the memory saved by using the flyweight pattern will be a considerable amount
3. jQuery and the Flyweight Pattern
The example is JAMES PADOLSEY's jQuery.single, as follows:
jQuery.single = (function(o){
var collection = jQuery([1]); // Fill with 1 item, to make sure length === 1
return function(element) {
// Give collection the element:
collection[0] = element;
// Return the collection:
return collection;
};
}());
// window.$_ = jQuery.single; // 定义别名
// test
jQuery('a').click(function(){
var html = jQuery.single(this).next().html(); // Method chaining works!
alert(html);
// etc. etc.
});
Maintains a singleton collection, avoiding the memory consumption caused by wrapping the same DOM object multiple times with $() (will create multiple jQuery objects), using jQuery.single will only create one jQuery object forever, saving the time consumed by creating extra jQuery objects, and also reducing memory overhead, but the biggest problem with doing this may be: the object returned by jQuery.single cannot be cached. Because it is a singleton implementation internally, the cached object may be changed after the next call to jQuery.single, so it cannot be cached at any time like $(). But it is said that using jQuery.single directly to get the singleton is faster than caching ordinary jQuery objects, but to avoid confusion, it is recommended to only use the jQuery.single method when you need to wrap a DOM object into a jQuery object
4. Pros and Cons of the Flyweight Pattern
Pros
-
Reduce memory overhead
-
Provide a convenient way to manage a large number of similar objects
Cons
-
The flyweight pattern needs to separate intrinsic state and extrinsic state, which will make the logic more complex
-
After the extrinsic state is separated, access will generate a slight extra time consumption (time for space?)
P.S. If a large number of instance objects need to be created in the project, you should consider whether the flyweight pattern is applicable
References
-
"JavaScript Design Patterns"
-
Flyweight Pattern: Provides a good example
No comments yet. Be the first to share your thoughts.