zoukankan      html  css  js  c++  java
  • javascript设计模式学习之十二——享元模式

    一、享元模式的定义及使用场景

    享元模式是为了解决性能问题而诞生的设计模式,这和大部分设计模式为了提高程序复用性的原因不太一样,果系统中因为创建了大量类似对象而导致内存占用过高,享元模式就非常有用了。

    享元模式的关键是区分内部状态和外部状态,剥离了外部状态的对象成为共享对象。有多少种内部状态的组合,系统中便最多存在多少个共享对象。而外部状态存在于共享对象的外部,在必要时被传入共享对象来组成一个完整的对象。一般情况下,以下情况发生时,可以使用享元模式:

    1)一个程序使用了大量的类似对象;

    2)由于使用了大量对象,造成了很大的内存开销;

    3)对象的大部分状态都可以变为外部状态;

    剥离出对象的外部状态后,可以用相对较少的共享对象取代大量对象。

    二、享元模式使用案例

    享元模式中通常存在这样的角色:

    1)对象工厂:当共享对象真正被需要时,才从工厂中生产出来;

    2)管理器,使用管理器来记录对象相关的外部状态,使得这些外部状态通过某个钩子和共享对象联系起来。

    以文件上传为案例进行分析,web上传一般支持多种方式,如浏览器插件,flash和表单上传等。为了简化例子,假设只有插件和Flash两种方式,无论是插件上传,还是Flash上传,原理都一样,当用户选择了文件之后,插件和Flash都会通知调用window下的一个全局Javascript函数,它的名字是startUpload,用户选择的文件列表被组合成一个数组files塞进该函数的参数列表中,代码如下:

        var id=0;
        window.startUpload=function(uploadType,files){
            for(var i=0,len=files.length;i<len;i++){
                var curFile=files[i];
                var uploadObj=new Upload(uploadType,curFile.fileName,curFile.fileSize);
                uploadObj.init(id++);
            }
        };

    可见,如果同时选择2000个文件,就会在程序中同时new了2000个upload对象,结果可想而知,浏览器很可能进入假死状态。

    对上面的情况使用享元模式进行改造,uploadType作为内部状态,其他外部状态可以剥离出来。

    //享元模式学习
        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 uploadPool={};
            return {
                create:function(uploadType){
                    if(uploadPool[uploadType]){
                        return uploadPool[uploadType];
                    }
                    return uploadPool[uploadType]=new Upload(uploadType);
                },
            };
        })();
        
        //使用管理器封装外部状态
        
        var uploadManager=(function(){
            //保存所有upload对象的外部状态
            var uploadDatabase={};
            return {
                add:function(id,uploadType,fileName,fileSize){
                    var uploadObj=uploadFactory.create(uploadType);
                    var dom=document.createElement('div');
                    dom.innerHTML='文件名称'+fileName+',文件大小'+fileSize+'<button class="delFile">删除</button>';
                    dom.querySelector('.delFile').onclick=function(){
                        uploadObj.delFile(id);
                    };
                    
                    uploadDatabase[id]={
                        fileName:fileName,
                        fileSize:fileSize,
                        dom:dom
                    };
                    return uploadObj;
                },
                
                setExternalState:function(id,obj){
                    var temp=uploadDatabase[id];
                    for(var key in temp){
                        obj[key]=temp[key];
                    }
                };
            };
        })();
        
        //此时触发上传的函数变成
        var id=0;
        window.startUpload=function(uploadType,files){
            for(var i=0,len=files.length;i<len;i++){
                var curFile=files[i];
                var uploadObj=uploadManager.add(++id,uploadType,curFile.fileName,curFile.fileSize);
                
            }
        };

    可见,此时,不论上传多少文件,都仅需要创建两个uploadObj对象即可,系统性能得到了较大提升。

    三、享元模式的进一步讨论

    1)没有内部状态的享元模式

    试想,如果系统中只有Flash或者插件一种上传方式,那么产生对象的工厂就变成了单例模式

        //如果没有内部状态,则生产对象的工厂实质上就变成了单例工厂,此时共享对象没有内部状态的区分,不过还是有剥离外部状态的过程,依旧倾向于称之为享元模式
        var uploadFactory=(function(){
            var uploadObj;
            return{
                create:function(){
                    if(uploadObj){
                        return uploadObj;
                    }
                        return uploadObj=new Upload();            
                    }
                }
    
        })();

    2)没有外部对象的享元模式

    java,c#中的字符串,对象池(http连接池,数据库连接池)都属于这种情况,在web前端中,对象池使用最多的是dom有关的操作。对象池和享元模式的思想有点像,但因为没有剥离外部对象的过程,一般不称之为享元模式。下面是一个通用的对象池代码实现:

    var objectPoolFactory=function(createObjFn){
            var objPool=[];
            return{
                create:function(){
                    var obj=objPool.length==0?createObjFn.apply(this,arguments):objPool.shift();
                    return obj;
                },
                recover:function(obj){
                    objPool.push(obj);
                },
            };
        }
  • 相关阅读:
    Angular6实现点击表格当前项判断批量操作按钮是否可以操作
    Angular实现全选后的取消其中一个选项则不能实现全选
    Angular6实现复选框的全选
    vue+element-ui 实现数据的增删改查以及分页(举例新增学生)
    vue基于element-ui制作的成绩管理系统(四)个人信息
    vue基于element-ui制作的成绩管理系统(三)主界面 登录后跳转的页面
    vue基于element-ui制作的成绩管理系统(二)登录页
    vue基于element-ui制作的成绩管理系统(二)搭建开发系统
    vue基于element-ui制作的成绩管理系统(-)设计思路
    git版本管理与github
  • 原文地址:https://www.cnblogs.com/bobodeboke/p/5660908.html
Copyright © 2011-2022 走看看