一个例子
假设你有一个服装工厂,生产了50件不同的男装和50件不同的女装,现在需要找一些模特来拍照,正常情况下,我们会招50个男模特和50个女模特,然后每人穿上1件来拍照,所以代码可能会写成这样。
代码语言:javascript复制var Model = function( sex, underwear){
this.sex = sex;
this.underwear= underwear;
};
Model.prototype.takePhoto = function(){
console.log( 'sex= ' this.sex ' underwear=' this.underwear);
};
for ( var i = 1; i <= 50; i ){
var maleModel = new Model( 'male', 'underwear' i );
maleModel.takePhoto();
};
for ( var j = 1; j <= 50; j ){
var femaleModel= new Model( 'female', 'underwear' j );
femaleModel.takePhoto();
};
写到这里,我们的性能其实已经有点差了,假如有2000服装,我们的程序可能早就崩溃了,所以我们需要进行优化,不难发现,我们对模特其实并没有要求,我们只想得到最终的照片,所以我们其实只需要找两位模特来拍照就好。
代码语言:javascript复制//这里不再直接给模特分配服装
var Model = function( sex ){
this.sex = sex;
};
Model.prototype.takePhoto = function(){
console.log( 'sex= ' this.sex ' underwear=' this.underwear);
};
//分别创建一个男模特对象和一个女模特对象:
var maleModel = new Model( 'male' ),
femaleModel = new Model( 'female' );
//给男模特依次穿上所有的男装,并进行拍照:
for ( var i = 1; i <= 50; i ){
maleModel.underwear = 'underwear' i;
maleModel.takePhoto();
};
//同样,给女模特依次穿上所有的女装,并进行拍照:
for ( var j = 1; j <= 50; j ){
femaleModel.underwear = 'underwear' j;
femaleModel.takePhoto();
};
使用场景
享元(flyweight)模式是一种用于性能优化的模式,。享元模式的核心是运用共享技术来有效支持大量细粒度的对象。
如果系统中因为创建了大量类似的对象而导致内存占用过高,享元模式就非常有用了。在 JavaScript 中,浏览器特别是移动端的浏览器分配的内存并不算多,如何节省内存就成了一件非常有意义的事情.
区分内部状态和外部状态
使用享元模式的关键就是区分它的内部状态和外部状态,内部状态就是在对象里通过内部方法管理,而外部信息可以在通过外部删除或者保存。
在上面的例子中,性别是内部状态,内衣是外部状态,通过区分这两种状态,大大减少了系 统中的对象数量。通常来讲,内部状态有多少种组合,系统中便最多存在多少个对象。
如何区分内部状态和外部状态,给出了几种情况
- 内部状态存储于对象内部。
- 内部状态可以被一些对象共享。
- 内部状态独立于具体的场景,通常不会改变。
- 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享
下面我们来实现一下具体的逻辑
借书系统
现在有一堆书籍,每一种书都有多本,我们需要设计一个系统来管理。需要包含以下功能:
- 借阅图书
- 返还图书
- 延长图书借阅时间
抽象它的内部状态和外部状态
虽然每一本书都是独立的,但是它们有自己的类别,我们可以认为,相同类别的书,共享一种内部状态。
而借阅操作则会随着不同用户,不同时间而产生变化,所以我们可以认为它是外部状态。
实现
首先我们实现图书类,这里只需要列出它的信息
代码语言:javascript复制 function Book(title, author) {
this.title = title;
this.author = author;
this.bookCount = 0;
//...
}
然后我们创建一个管理图书的工厂,所有的图书都需要从这里创建,每一个种图书不论多少本,只创建一个对象.这里添加了一个getBookCount的方法,方便获取每一种书的数量。
代码语言:javascript复制 var bookFactory = (function () {
var existingBooks = {};
return {
createBook: function (title, author) {
/*查找之前是否创建*/
console.log(existingBooks);
var existingBook = existingBooks[title];
if (existingBook) {
//如果存在则数量加一
existingBook.bookCount = 1;
return existingBook;
} else {
/* 如果没有,就创建一个,然后保存*/
var book = new Book(title, author);
book.bookCount = 1;
existingBooks[title] = book;
return book;
}
},
getBookCount:function(title){
return existingBooks[title].bookCount;
}
}
})();
最后就是管理外部状态了,我们还是需要构造一个管理对象,将借阅和退还定义成它的方法,然后将所有外部状态汇集到一起。
代码语言:javascript复制 //管理外部状态
var bookManager = (function () {
var bookRecordDatabase = {};
return {
addBookRecord: function (id, title,dateline) {
if(bookRecordDatabase[id]){
console.log("已经存在借阅了哦");
return;
}
if (this.checkIsBookRest(title)) {
var record = {
id, title, dateline
};
bookRecordDatabase[id] = record;
console.log("你借阅了",title);
} else {
console.log("该书已经借阅完了哦");
}
},
extendDateLine: function (id) {
bookRecordDatabase[id].dateline = 5;
console.log("你延长了该书的日期",bookRecordDatabase[id].title);
},
//检查该种书被借阅完没有
checkIsBookRest: function (title) {
var count = 0;
for (a in bookRecordDatabase) {
record = bookRecordDatabase[a]
if (record.title == title) {
count ;
}
}
return count < bookFactory.getBookCount(title);
},
returnBook: function (id) {
delete bookRecordDatabase[id];
}
}
})();
小小的测试一下
代码语言:javascript复制 var books = [
{
title: "JS从入门到精通",
author: "X",
},
{
title: "JS从入门到精通",
author: "X",
},
{
title: "JS从入门到精通",
author: "X",
},
{
title: "JS权威指南",
author: "Y",
},
{
title: "JS权威指南",
author: "Y",
},
];
for(var i = 0 ; i < books.length ; i ){
bookFactory.createBook(books[i].title,books[i].author);
}
bookManager.addBookRecord(1,"JS权威指南",5);
bookManager.addBookRecord(2,"JS从入门到精通",5);
bookManager.addBookRecord(3,"JS权威指南",5);
bookManager.addBookRecord(4,"JS权威指南",5);
bookManager.extendDateLine(2);
bookManager.returnBook(3);
bookManager.addBookRecord(5,"JS权威指南",4);
结果
享元模式的适用性
使用了享元模式之后,我们需要分别多维护一个 factory 对象和一个 manager 对 象,在大部分不必要使用享元模式的环境下,这些开销是可以避免的。
享元模式带来的好处很大程度上取决于如何使用以及何时使用,一般来说,以下情况发生时 便可以使用享元模式。
- 一个程序中使用了大量的相似对象。
- 由于使用了大量对象,造成很大的内存开销。
- 对象的大多数状态都可以变为外部状态。
- 剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象
最后
本文简单的介绍了享元模式,享元模式是为解决性能问题而生的模式,这跟大部分模式的诞生原因都不一样。在一个存在大量相似对象的系统中,享元模式可以很好地解决大量对象带来的性能问题