js常用设计模式1-单例模式
js常用设计模式2-策略模式
js常用设计模式3-代理模式
js常用设计模式4-发布-订阅模式
js常用设计模式5-命令模式
js常用设计模式6-组合模式
js常用设计模式7-享元模式
js常用设计模式8-职责链模式
js常用设计模式9-中介者模式
js常用设计模式10-装饰者模式
js常用设计模式11-状态模式
享元模式是一种用于性能优化的模式,核心是运用共享技术来有效支持大量细粒度的对象。
如果系统中创建了大量类似的对象,导致内存占用过高,这时候享元模式就很有用了。
1,假发工厂
假设有一个假发工厂,生产50种男士假发和50种女士假发,这时候我们需要50个男模和50个女模,分别套上假发,然后拍照:
var Model = function (sex, underware) {
this.sex = sex
this.underware = underware
}
Model.prototype.takePhoto = function () {
console.log('sex:' + this.sex + 'underware:' + this.underware)
}
for (var i = 0; i < 50; i++) {
var maleModel = new Model('男', 'underware' + i)
maleModel.takePhoto()
}
for (var i = 0; i < 50; i++) {
var femaleModel = new Model('女', 'underware' + i)
femaleModel.takePhoto()
}
很明显,这种做法略显沙雕。
2,享元模式优化
实际上,我们只需要两个模特就够了,假发换着套就行。
//效果一样,感觉好多了
var Model = function (sex) {
this.sex = sex
}
Model.prototype.takePhoto = function () {
console.log('sex:' + this.sex + 'underware:' + this.underware)
}
var maleModel = new Model('男')
var femaleModel = new Model('女')
for (var i = 0; i < 50; i++) {
maleModel.underware = 'underware' + i
maleModel.takePhoto()
}
for (var i = 0; i < 50; i++) {
femaleModel.underware = 'underware' + i
femaleModel.takePhoto()
}
3,内部状态与外部状态
享元模式的目标是尽量减少共享对象的数量:
(1)内部状态存储于对象内部
(2)内部状态可以被一些对象共享
(3)内部状态独立于具体的场景
(4)外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享
享元模式是一种时间换空间的优化模式
4,普通版本上传文件的例子
现在我们要实现一个功能:往服务器上传文件。假设目前只有两种上传方式:插件上传和flash上传。这俩实现是一样的,选择文件之后,都会调用window下的startUpload方法,用户选择的文件列表被组合成一个数组files放到该函数的参数列表中。
代码如下:
var id = 0;
//uploadType区分是插件上传还是flash
window.startUpload = function (uploadType, files) {
for (var i = 0, file; file = files[i++];) {
var upload = new Upload(uploadType, file.fileName, file.fileSize)
upload.init(id++)
}
}
用户选择完文件后,startUpload函数会遍历files数组,挨个创建对应的upload对象。现在我们要定义Upload构造函数,除了初始化init函数之外,我们还需要一个删除函数delFile。代码如下:
// uploadType:上传类型, fileName:文件名, fileSize:文件大小
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)
}
}
现在,让我们来try一try:
startUpload('plugin', [
{
fileName: '1.txt',
fileSize: 1000
},
{
fileName: '2.txt',
fileSize: 3000
},
{
fileName: '3.txt',
fileSize: 5000
}
])
startUpload('flash', [
{
fileName: '4.txt',
fileSize: 1000
},
{
fileName: '5.txt',
fileSize: 3000
},
{
fileName: '6.txt',
fileSize: 5000
}
])
5,享元模式优化文件上传
上面这个上传功能,有多少个需要上传的对象,就创建了多少个upload对象,非常的浪费。下面,我们用享元模式来重构它。
(1)首先,要明确内部状态
upload对象依赖于upLoadType属性,由于我们现在是简化版的上传,因此只要明确了upLoadType,这个upload对象是可以被其他文件共享的。而fileName、fileSize都不一样,因此这俩是外部状态。
(2)剥离外部状态
现在,我们的Upload构造函数中,只需要upLoadType这一个参数就可以:
var Upload = function (uploadType) {
this.uploadType = uploadType
}
Upload.prototype.init函数也不需要了,现在upload对象的初始化被放在了uploadManager.add这个函数中。
接下来我们只需要定义Upload.prototype.delFile函数就ok:
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)
}
}
(3)工厂函数进行对象实例化
定义一个工厂,用来创建upload对象,如果某种内部状态(upLoadType)的对象已经被创建过,那么直接返回这个对象,否则创建新的对象:
var UploadFactory = (function () {
var createdFlyWeightObjs = {}
return {
create: function (uploadType) {
if (createdFlyWeightObjs[uploadType]) {
return createdFlyWeightObjs[uploadType]
}
return createdFlyWeightObjs[uploadType] = new Upload(uploadType)
}
}
})()
(4)管理器封装外部状态
在(2)中我们提到了uploadManager对象,这个对象负责向UploadFactory提交创建对象的请求,并用一个uploadDatabase对象保存所有的upload对象的外部状态,以便在程序运行中给upload共享对象设置外部状态:
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)
}
}
让我们try一try最后的效果:
startUpload('plugin', [
{
fileName: '1.txt',
fileSize: 1000
},
{
fileName: '2.txt',
fileSize: 3000
},
{
fileName: '3.txt',
fileSize: 5000
}
])
startUpload('flash', [
{
fileName: '4.txt',
fileSize: 1000
},
{
fileName: '5.txt',
fileSize: 3000
},
{
fileName: '6.txt',
fileSize: 5000
}
])
6,小结
享元模式是一个很好的性能优化方案,但是因为多了uploadManager和UploadFactory 对象,也会带来维护问题。
享元模式适用场景:
- 一个程序中大量使用相似对象
- 对于使用大量对象,造成了很大的内存开销
- 对象的大多数状态可以变为外部状态
- 剥离对象的外部状态后,可以用较少的共享对象取代大量对象
网友评论