享元模式,结构型设计模式之一,运用共享内部状态/外部状态来减少创建对象的数量,从而减少内存占用、提高性能。
享元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生产大量细粒度的类实例来表示数据,把那些参数移动到类实例的外面,在方法调用的时候将他们传递进来,就可以通过共享大幅度第减少单个实例的数目。
使用场景:
第一种是应用在数据层上,主要是应用在内存里大量相似的对象上;
第二种是应用在DOM层上,享元可以用在中央事件管理器上用来避免给父容器里的每个子元素都附加事件句柄。
实战一:
我们常见的面向对象的写法,就是像下面这样,一股脑的把对象的所有属性和方法,全部列出出来,然后再不断的new,要多少实例就new多少次,这种粗暴的方式,会占据大量的内存,应用很卡。
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;
},
/*其它get方法在这里就不显示了*/
// 更新借出状态
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;
},
//是否到期
isPastDue: function(bookID){
var currentDate = new Date();
return currentDate.getTime() > Date.parse(this.dueReturnDate);
}
};
我们都知道,构造函数里面的属性是每个实例各自所有的,但是一个对象的属性,一本书为例,它的页码,书名,作者,版本,ISBN等都是固有的,不管谁买去用,书就是这本书,我们可以把这些属性作为内部属性/数据, 像谁买了,买去干什么了,什么时候卖掉的的,这是可以作为外部数据,是变动的。
所以我们利用享元模式优化如下:
//书就是那本书
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;
};
//会有很多书的实例不断穿件,所以我们需要借助工厂方法和单例
然后创一个管理书的状态的方法类
/*BookRecordManager 借书管理类 单例*/
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
};
return book;
},
getBook:function(id){
return bookRecordDatabase[id].book;
},
getTitle:function(id){
return bookRecordDatabase[id].book.title
},
getAuthor:function(id){
return bookRecordDatabase[id].book.author
},
getDueReturnDate:function(id){
return bookRecordDatabase[id].dueReturnDate
},
updateCheckoutStatus: function(bookID, newStatus, checkoutDate, checkoutMember,newReturnDate){
var record = bookRecordDatabase[bookID];
record.availability = newStatus;
record.checkoutDate = checkoutDate;
record.checkoutMember = checkoutMember;
record.dueReturnDate = newReturnDate;
return record;
},
extendCheckoutPeriod: function(bookID, newReturnDate){
bookRecordDatabase[bookID].dueReturnDate = newReturnDate;
},
isPastDue: function(bookID){
var currentDate = new Date();
return currentDate.getTime() > Date.parse(bookRecordDatabase[bookID].dueReturnDate);
}
};
})();
//最后我们来使用
var a = BookRecordManager.addBookRecord(1,"寻秦记", "古龙", 2,30000,0001,"ISBN-1000","checkoutDate", "checkoutMember", "dueReturnDate", "availability")
BookRecordManager.addBookRecord(2,"神雕侠侣", "金庸", 1,20000,0001,"ISBN-2010","checkoutDate000", "checkoutMember000", "dueReturnDate000", "availability000")
var b = BookRecordManager.updateCheckoutStatus(1,"checkoutDate1111", "checkoutMember1111", "dueReturnDate1111", "availability1111")
var c = BookRecordManager.extendCheckoutPeriod(1,"dueReturnDate2222")
console.log(BookRecordManager.getBook(1))
console.log(BookRecordManager.getTitle(1))
console.log(BookRecordManager.getAuthor(1))
console.log(BookRecordManager.getDueReturnDate(1))
console.log(BookRecordManager.getBook(2))
console.log(BookRecordManager.getTitle(2))
console.log(BookRecordManager.getAuthor(2))
另外一个案例
class ObjectPool{
constructor(){
this._pool = []
}
create(Obj){
return this._pool.length === 0 ? new Obj(this):this._pool.shift();
}
recover(obj){
return this._pool.push(obj)
}
size(){
return this._pool.length;
}
}
class File{
constructor(pool){
this.pool = pool;
}
download(){
console.log(`从${this.src} 开始下载 ${this.name}`);
setTimeout(() => {
console.log(`- ${this.name}下载完毕`)
this.pool.recover(this)
}, 100);
}
}
let objPool = new ObjectPool()
let file1 = objPool.create(File);
file1.name = '文件1';
file1.src="http://download1.com"
file1.download()
let file2 = objPool.create(File);
file2.name = "文件2";
file2.src = "https://download2.com";
file2.download();
setTimeout(() => {
let file3 = objPool.create(File);
file3.name = "文件3";
file3.src = "https://download3.com";
file3.download();
}, 200);
setTimeout(() => {
let file3 = objPool.create(File);
file3.name = "文件3";
file3.src = "https://download3.com";
file3.download();
}, 200);
setTimeout(
() =>
console.log(
`${"*".repeat(50)}\n下载了3个文件,但其实只创建了${objPool.size()}个对象`
),
1000
);
本系列所有的案例均来自网络