JavaScript设计模式_09_享元模式

享元模式属于一种性能优化模式。当前端需要创建大量的js对象时,可以考虑使用这种模式进行优化。尤其是移动端,对内存的占用要求比较高,所以,如何节省内存就变成了一件非常有意义的事情。

/**
 * pre:享元模式:一种性能优化模式
 * 使用共享技术来有效支持大量细粒度的对象
 */
//--------- 示例1 -----------
/*
 * 有一家内衣工厂,生产了50种男士内衣和50种女士内衣。为了推销产品,他们决定用塑料模特拍广告进行推广。
 * 正常情况下,需要用50个男模特和50个女模特,分别为他们穿上一款内衣进行拍照。
 * 为了减少创建模特的开销,我们这里使用享元模式,建立1个男模特和1个女模特。
 * 以下是示例程序:
 */
var model = function(sex) {
    this.sex = sex;
    this.underwear = null;
};
model.prototype.setUnderwear = function(underwear) {
    this.underwear = underwear;
};
model.prototype.takePhoto = function() {
    console.log("this sex : " + this.sex + ",this underwear : " + this.underwear);
};

var maleModel = new model("male");
for(var i = 1; i <= 50; i++) {
    maleModel.setUnderwear("underwear" + i);
    maleModel.takePhoto();
}
var femaleModel = new model("female");
for(var i = 1; i <= 50; i++) {
    femaleModel.setUnderwear("underwear" + i);
    femaleModel.takePhoto();
}

//---------- 示例2 -------------
/*
 * 有一个文件上传模块,同时支持多个文件上传,上传方式分为插件、和flash两种方式,并要求上传的文件具有删除功能。
 * 我们为每一个上传文件创建一个upload对象,通过操作这个对象来实现删除的功能。
 * 以下是示例程序:
 */
var id = 0;
window.startUpload = function(uploadType, files) {
    for(var i = 0, a; a = files[i++];) {
        var uploadObj = new Upload(uploadType, a.fileName, a.fileSize);
        uploadObj.init(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 _self = 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() {
        _self.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);
    }
};

startUpload("plugin", [{
    fileName: "1.txt",
    fileSize: 1000
}, {
    fileName: "2.html",
    fileSize: 3000
}, {
    fileName: "3.pdf",
    fileSize: 5000
}])

startUpload("flash", [{
    fileName: "4.txt",
    fileSize: 1000
}, {
    fileName: "5.html",
    fileSize: 3000
}, {
    fileName: "6.pdf",
    fileSize: 5000
}]);

//------------ 示例3 ----------------
/**
 * 为了减少对象的创建,我们使用享元模式,对以上代码进行重构。
 * 示例如下:
 */
var id = 0;
var Upload = function(uploadType) {
    this.uploadType = uploadType;
};
Upload.prototype.delFile = function(id) {
    manager.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 createUploadFactory = (function() {
    var objCache = {};
    return function(uploadType) {
        if(objCache[uploadType]) {
            return objCache[uploadType];
        }
        return objCache[uploadType] = new Upload(uploadType);
    }
})();
var manager = (function() {
    var uploadCache = {};
    var add = function(id, uploadType, fileName, fileSize) {
        var uploadObj = createUploadFactory(uploadType);
        var dom = document.createElement("div");
        dom.innerHTML = "<span>文件名称:" + fileName + ",文件大小:" + fileSize + "</span>" + "<button class='delFile'>删除</button>";
        dom.querySelector(".delFile").onclick = function() {
            uploadObj.delFile(id);
        };
        document.body.appendChild(dom);
        uploadCache[id] = {
            fileName: fileName,
            fileSize: fileSize,
            dom: dom,
            id: id
        };
        return uploadObj;
    };
    var setExternalState = function(id, obj) {
        var uploadObj = uploadCache[id];
        for(var i in uploadObj) {
            obj[i] = uploadObj[i];
        }
    };
    return {
        add: add,
        setExternalState: setExternalState
    }
})();
window.startUpload = function(uploadType, files) {
    for(var i = 0, a; a = files[i++];) {
        manager.add(id++, uploadType, a.fileName, a.fileSize);
    }
}

startUpload("plugin", [{
    fileName: "1.txt",
    fileSize: 1000
}, {
    fileName: "2.html",
    fileSize: 3000
}, {
    fileName: "3.pdf",
    fileSize: 5000
}])

startUpload("flash", [{
    fileName: "4.txt",
    fileSize: 1000
}, {
    fileName: "5.html",
    fileSize: 3000
}, {
    fileName: "6.pdf",
    fileSize: 5000
}]);
/**
 * 通过以上的例子,我们可以看出,享元模式把对象的属性进行了缓存,当我们真正需要操作对象的时候,再给对象赋值相应的属性,从而达到一种多态,减少对象创建的效果。
* 另外,需要注意的是,使用享元模式也带来了一些复杂性问题。比如上面的例子中我们需要维护factory和manager这两个对象,在大部分不需要使用享元模式的情况下,这个开销可以避免 * 。一般来说以下情况发生时可以使用享元模式:
* 1、一个程序中使用了大量相似的对象;
* 2、由于使用了大量对象,造成了内存开销过大;
* 3、对象大多数的状态可变为外部状态;
* 4、剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。
*/
posted @ 2017-06-20 15:05  Stinchan  阅读(162)  评论(0编辑  收藏  举报