设计模式之享元模式
什么是享元模式
享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类)。
文件上传的例子
上传文件方法比如浏览器插件、Flash 和表单上传等,为了简化例子,我们先假设只有插件和 Flash 这两种。不论是插件上传,还是 Flash 上传,原理都是一样的,当用户选
择了文件之后,插件和 Flash 都会通知调用 Window 下的一个全局 JavaScript 函数,它的名字是startUpload,用户选择的文件列表被组合成一个数组 files 塞进该函数的参数列表里,代码如下:
var id = 0;
window.startUpload = function( uploadType, files ){ // uploadType 区分是控件还是 flash
for ( var i = 0, file; file = files[ i++ ]; ){
var uploadObj = new Upload( uploadType, file.fileName, file.fileSize );
uploadObj.init( id++ ); // 给 upload 对象设置一个唯一的 id
}
};
当用户选择完文件之后,startUpload 函数会遍历 files 数组来创建对应的 upload 对象。接下来定义 Upload 构造函数,它接受 3 个参数,分别是插件类型、文件名和文件大小。这些信息都已经被插件组装在 files 数组里返回,代码如下:
var Upload = function( uploadType, fileName, fileSize ){
this.uploadType = uploadType;
this.fileName = fileName;
this.fileSize = fileSize;
this.dom= null;
};
Upload.prototype.init = function( id ){
var that = this;
this.id = id;
this.dom = document.createElement( 'div' );
this.dom.innerHTML =
'<span>文件名称:'+ this.fileName +', 文件大小: '+ this.fileSize +'</span>' +
'<button class="delFile">删除</button>';
this.dom.querySelector( '.delFile' ).onclick = function(){
that.delFile();
}
document.body.appendChild( this.dom );
};
Upload.prototype.delFile = function(){
if ( this.fileSize < 3000 ){
return this.dom.parentNode.removeChild( this.dom );
}
if ( window.confirm( '确定要删除该文件吗? ' + this.fileName ) ){
return this.dom.parentNode.removeChild( this.dom );
}
};
接下来分别创建 3 个插件上传对象和 3 个 Flash 上传对象:
startUpload( 'plugin', [
{
fileName: '1.txt',
fileSize: 1000
},
{
fileName: '2.html',
fileSize: 3000
},
{
fileName: '3.txt',
fileSize: 5000
}
]);
startUpload( 'flash', [
{
fileName: '4.txt',
fileSize: 1000
},
{
fileName: '5.html',
fileSize: 3000
},
{
fileName: '6.txt',
fileSize: 5000
}
]);
享元模式重构文件上传:
明确了 uploadType 作为内部状态之后,我们再把其他的外部状态从构造函数中抽离出来,Upload 构造函数中只保留 uploadType 参数:
var Upload = function( uploadType){
this.uploadType = uploadType;
};
Upload.prototype.delFile = function( id ){
uploadManager.setExternalState( id, this ); // (1)
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( uploadType);
}
}
})();
// 管理器封装外部状态
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 ];
}
}
}
})();
// 然后是开始触发上传动作的 startUpload 函数:
var id = 0;
window.startUpload = function( uploadType, files ){
for ( var i = 0, file; file = files[ i++ ]; ){
var uploadObj = uploadManager.add( ++id, uploadType, file.fileName, file.fileSize );
}
};
startUpload( 'plugin', [
{
fileName: '1.txt',
fileSize: 1000
},
{
fileName: '2.html',
fileSize: 3000
},
{
fileName: '3.txt',
fileSize: 5000
}
]);
startUpload( 'flash', [
{
fileName: '4.txt',
fileSize: 1000
},
{
fileName: '5.html',
fileSize: 3000
},
{
fileName: '6.txt',
fileSize: 5000
}
]);