享元模式是一种性能优化模式,其核心是运用共享技术来有效的支持大量细粒度的对象。如果系统中因为创建大量类似的对象而导致内存占用过高,那么享元模式就非常有用了。享元模式的目标是尽量减少共享对象的数量。
一、那么什么是享元模式呢?
享元模式要求将对象的属性划分为内部状态和外部状态。那如何划分外部状态和内部状态呢?
1、内部状态存储于对象内部;内部状态可以被一些对象共享;内部状态独立于具体的场景,通常不会改变。
2、外部状态取决于具体的场景,并根据场景而变化,外部状态不被共享。
内部状态相同的对象指定为同一对象,而外部状态可以从同一对象的身上分开,并存储在外部的对象身上。内部状态有多少种情况,系统中便存在多少个对象。
使用享元模式的关键在于如何区分外部状态和内部状态。
二、享元模式的通用结构
1、当对象被需要的时候才创建,所以需要一个对象工厂来创建对象。
2、对象外部状态的管理器来记录对象相关的外部状态。
3、对象本身保存内部状态。
接下来看一段代码:
//抛离外部状态的对象
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 UploadFactory=(function(){
var createdFlyWeightObjs={};
return {
create:function(uploadType){
if(createdFlyWeightObjs[uploadType]){
return createdFlyWeightObjs[uploadType]
}
return createdFlyWeightObjs[uploadType]=new Upload();
}
}
})();
//对象外部管理器
var uploadManager=(function(){
var uploadDatabase={};
return {
add:function(id,uploadType,fileName,fileSize){
var flyWeightObj=UploadFactory.create(uploadType);
var dom=document.createElement('div');
var str=`
<span>文件名称${fileName}</span>
<span>文件大小${fileSize}</span>
<span class="delFile">删除</span>
`;
document.body.innerHTML=str;
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;
for(var i in uploadData){
flyWeightObj[i]=uploadData[i];
}
}
}
}())
享元模式的适用:
1、一个程序中使用大量的相似的对象
2、由于使用大量的对象,造成大的内存开销
3、对象的大多数状态可变成外部状态
4、抛离外部状态之后可以相对较少的共享对象取代大量对象。
那如果没有外部状态和内部状态的享元呢?
1、没有内部状态的享元其实就是一个唯一的一个共享对象,那么就用单例模式来创建对象,对象的外部管理器不发生改变。
2、没有外部状态我们就要学习对象池
什么是对象池?
对象池维护一个装载空闲对象的池子,如果需要对象的时候,不是直接new,而是转从对象池里获取,如果对象池里没有空闲的对象,则创建一个新的对象,当获取出的对象完成它的职责之后,再进入池子等待被下次获取。
web前端开发,对象池用于最多的就是跟DOM节点有关的操作。比如分页的DOM生成。如何避免频繁的创建和删除DOM节点。
对象池是另一种性能优化方案,它和享元模式一些相似,但并不区分外部内部状态这个过程。
//例子:
var tool=(function(){
var arr=[];
return{
create:function(){
if(tool.length===0){
var div=document.createElement('div');
document.body.appendChild(div);
return div;
}else{
return arr.shift();
}
},
recover:function(dom) {
return arr.push(dom);
}
}
}());
var ary=[];
for(var i=0,str;str=['a','b'][i++];){
var t=tool.create();
t.innerHTML=str;
ary.push(t);
}
//通用的对象池实现
var objectPoolFactory=function (createObjFn) {
var objectPool=[];
return {
create:function () {
var obj=objectPool.length===0?createObjFn.apply(this.arguments):objectPool.shift();
return obj;
},
recover:function () {
objectPool.push(obj);
}
}
}