[TOC] >[success] # 代码案例 * 享元模式的构成 ~~~ 'Flyweight':是抽象享元角色,为具体享元角色规定了必须实现的方法。 'ConcreteFlyweight':是具体享元角色,实现抽象享元角色定义的方法。 'FlyweightFactory':是享元工厂,负责创建和管理享元角色,它用于构造一个池容器, 同时提供从池中获得对象的方法。 'Client':是客户端角色:维护对所有享元对象的引用,而且还需要存储对应的外蕴状态。 ~~~ >[danger] ##### 书中上传案例 ~~~ 1.书中举了一个文件上传的例子,作者思路是封装一个文件上传的类'Upload',这个类有一个 'init' 方法用来初始化上传文件信息,生成上传文件列表,'delFile' 方法如果文件尺寸大于3000kb 询问是否删除否则直接上传 2.这整个类的参数有'uploadType' 用来记录使用的那种上传方式,'fileName' 记录文件名, 'fileSize' 文件大小 3.上传文件都存在数组中去,然后会调用'startUpload '这个 'window'全局方法用来生成上传实例 ~~~ * 代码的问题 ~~~ 1.随着上传文件越来越多整个创建的实例也会越来越多,这时候就可以用享元模式来解决 ~~~ ~~~ var id = 0 /** * @param uploadType 使用上传对象的类型 * @param files 上传文件数组集合 * **/ window.startUpload = function (uploadType, files) { // 将文件保存进上传对应插件的实类中 for(var i=0,file;file=files[i++];){ var uploadObj = new Upload( uploadType, file.fileName, file.fileSize ); uploadObj.init( id++ ); // 给 upload 对象设置一个唯一的 id } } // 上传对象类 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 ); } // 删除小于3000KB 文件直接删除 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 ); } }; // 上传文件 ,两种上传方式插件上传 和 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 } ]); ~~~ >[info] ## 书中解决思路 ~~~ 1.使用享元模式,首先分析内外状态,分析的四个步骤: 1.1.内部状态储存于对象内部。 1.2.内部状态可以被一些对象共享。 1.3.内部状态独立于具体的场景,通常不会改变。 1.4.外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享。 2.开始分析 对象中的三个属性'uploadType, fileName, fileSize',其中'uploadType'类型 是固定可以看成为内部属性,就好比上个案例的性别一样属于固定类型个数的属性, 'fileName, fileSize' 属于动态变化归类为外部状态属性 ~~~ >[danger] ##### 只包含内部状态的上传类(第一部分) ~~~ 1.将内部元素封装成类的属性,这里要注意的是,'init'方法也已经不是内部类了,因为 现在使用享元模式后,不会在随着参数创建实例,而是会根据类型创建实例因此'init'方法 也失去了原本的意义,也需要配合不停改变的外部元素进行创建 ~~~ ~~~ 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 ); } }; ~~~ >[danger] ##### 创建工厂对实例化对象进行控制(第二部分) ~~~ 1.接下来定义工厂,用来缓存创建的对象,如果该类型的对象存在直接调用之前的对象 ~~~ ~~~ var UploadFactory = (function(){ var createdFlyWeightObjs = {}; return { create: function( uploadType){ if ( createdFlyWeightObjs [ uploadType] ){ return createdFlyWeightObjs [ uploadType]; } return createdFlyWeightObjs [ uploadType] = new Upload( uploadType); } } })(); ~~~ >[danger] ##### 对外部状态的管理(第三部分) ~~~ 1.刚才将原本创建对象的类'Upload ' 进行了打散的操作现在就需要在外部管理这个方法进行 重新的组装,来调用刚才的'工厂函数'因为'工厂函数'内部会去调用'Upload' 2.整个对外部元素创建的管理,做了两件事,一件事有一个add方法等同于之前实例中的'init' 方法,一个'setExternalState' 用将对应的创建对象和'Upload '中'delFile'方法进行关联,以做到 删除调用 ~~~ ~~~ 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 ]; } } } })() ~~~ >[danger] ##### 全局调用的方法(第四部分) ~~~ 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 ); } }; ~~~ >[danger] ##### 使用 ~~~ 1.无论怎么使用创建实例的个数永远都只会根据内部状态变量的改变个数决定 ~~~ ~~~ startUpload( 'plugin', [ { fileName: '1.txt', fileSize: 1000 }, { fileName: '2.html', fileSize: 3000 }, { fileName: '3.txt', fileSize: 5000 } ]); ~~~