享元模式
定义
享元模式:运用共享技术来有效地支持大量细粒度对象的复用,以减少创建的对象的数量。
享元模式的主要思想是共享细粒度对象,也就是说如果系统中存在多个相同的对象,那么只需共享一份就可以了,不必每个都去实例化每一个对象,这样来精简内存资源,提升性能和效率。
内部状态与外部状态
享元模式的目标是尽量减少共享对象的数量,关于如何划分内部状态和外部状态,下面的几条经验提供了一些指引:
- 内部状态存储于内部对象
- 内部状态可以被一些对象共享
- 内部状态独立于具体的场景,通常不会改变
- 外部状态取决于具体的场景,并根据具体的场景而变化,外部状态不能被共享
这样一来,我们便可以把所有内部状态相同的对象都指定为同一共享对象。而外部状态可以从对象身上剥离出来,并存储在外部。
示例
文件上传的例子,上传文件方式有插件、Flash 和 表单上传,当用户选择了文件之后,插件 和 Flash 会通知 Window 下的全局 JavaScript 函数,它的名字是 startUpload, 用户选择的文件列表被组合成一个数组 files 塞进该函数的参数列表里,代码如下:
// 剥离外部状态
var Upload = function(uploadType) {
this.uploadType = uploadType;
}
Upload.prototype.delFile = function(id){
uploadManager.setExternalState(id, this);
if(this.fileSize < 3000) {
return this.dom.parentNode.removeChild(this.dom);
}
if(window.confirm("确认要删除该文件吗?" + this.fileName)) {
return this.dom.parentNode.removeChild(this.dom);
}
}
// 管理器封装外部状态
var uploadManager = (function(){
var uploadDatabase = {};
return {
add: function(id, uploadType, fileName, fileSize) {
var flyWeightObj = UploadFactory.create(uploadType);
var dom = document.createElement('div');
dom.innerHTML =
'<span>文件名称:' + fileName + ',文件大小:' + fileSize + '</span>' +
'<button class="delFile">删除</button>';
dom.querySelector('.delFile').onclick = function(){
flyWeightObj.delFile(id);
}
document.body.appendChild(dom);
uploadDatabase[id] = {
fileName: fileName,
fileSize: fileSize,
dom: dom
}
return flyWeightObj;
},
setExternalState: function(id, flyWeightObj) {
var uploadData = uploadDatabase[id];
for(var i in uploadData) {
flyWeightObj[i] = uploadData[i];
}
}
}
})();
// 工厂对象实例化
var UploadFactory = (function(){
var createedFlyWeightObjs = {};
return {
create: function(uploadType) {
if(createedFlyWeightObjs[uploadType]) {
return createedFlyWeightObjs[uploadType]
}
return createedFlyWeightObjs[uploadType] = new Upload(uploadType);
}
}
})();
测试代码:
var id = 0;
window.startUpload = function(uploadType, files) {
for(var i = 0; i < files.length; i++) {
var file = files[i];
var uploadObj = uploadManager.add(++id, uploadType, file.fileName, file.fileSize);
}
};
startUpload('plugin',[
{
fileName: '1.txt',
fileSize: 1000
},
{
fileName: '2.txt',
fileSize: 4000
},
{
fileName: '3.txt',
fileSize:2000
}
]);
startUpload('flash',[
{
fileName: '4.txt',
fileSize: 1000
},
{
fileName: '5.txt',
fileSize: 4000
},
{
fileName: '6.txt',
fileSize:2000
}
]);
有几种上传方式就最多有几个对象。
总结
享元模式是为解决性能问题而生的模式,这跟大部分模式的诞生原因都不一样。在一个存在大量相似对象的系统中,享元模式可以很好地解决大量对象带来的性能问题。