享元模式(Flyweight)是一种软件设计模式.是通过将类似的对象进行共享,达到最小化内存的使用对象.

享元模式要求将对象的属性划分为内部状态(不会改变属性)和外部状态(会动态变化的属性).享元模式的目标是尽量减少共享对象的数量.它的关键点是划分内部状态和外部状态.

享元模式特点:

  • 内部状态存储于对象内部
  • 内部状态可以被一些对象共享
  • 内部状态独立于具体的场景,通常不会变化
  • 外部状态取决于具体的场景,并根据场景而变化,外部状态不会被共享.

注意: 享元模式是一种用时间换空间的优化模式.

例子

PC端的文件上传组件.文件上传有多种方式,相同的方式可以通过一个对象来完成,而不用根据调用次数创建多个类型相同的对象.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 上传类型工厂方法, 享元模式内部状态对象
var UploadFactory = (function () {
var createFlyWeightObjs = {};
return {
create (upload) {
if (createdFlyWeightObjs[uploadType]) {
return createdFlyWeightObjs[uploadType];
}
return createdFlyWeightObjs[uploadType] = new Upload(uploadType);
}
}
})

var Upload = function (uploadType) {
this.uploadType = uploadType
}
Upload.prototype.delFile = function (id) {
uploadManager.setExternalState(id, this);
this.dom.parentNode.removeChild(this.dom);
}

// 上传方式 方法
var uploadManager = (function () {
var uploadDatabase = {};
return {
add (id, uploadType, fileName, fileSize) {
var flyWeightObj = UploadFactory.create(uploadType);
dom.innerHTML = `<span>文件件名词: ${fileName},文件大小:${fileSize}</span>`+
`<button class="delFile">删除</button>`;
dom.querySelector('.delFile').onclick = function () {
flyWeightObj.delFile(id);
}
document.body.applendChild(dom);
uploadDatabase[id] = {
fileName,
fileSize,
dom
};
return flyWeightObj;
},
setExternalState (id, flyWeightObj) {
var uploadData = uploadDatabase[id];
for(let i in uploadData) {
flyWeightObj[i] = uploadData[i];
}
}
}
})();

window.startUpload = function (uploadType, files) {
for(let i = 0, file; file = files[i++]) {
var uploadObj = uploadManage.add(++id, uploadType, file.fileName, file.fileSize);
})
}

startUpload('plugin', [{fileName: '1.text',fileSize: 1000},{fileName: '2.png', fileSize: 1900}])

startUpload('flash', [{fileName: '3.text',fileSize: 1000},{fileName: '4.png', fileSize: 1900}])

适用范围

  • 一个程序中使用大量相似的对象
  • 由于使用了大量对象,造成很大的内存开销
  • 对象的大多数状态都可以变为外部状态
  • 剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量的对象.

对象池

对象池模式(object pool pattern)和享元模式有些相识.都是将共享对象节省创建对象的开销.

不同的是,对象池是将已经创建的对象集中在一起,当有地方需要的时候,从它这里获取对象使用.当对象使用完成后,再归还给对象池.

对象池(英语:object pool pattern)是一种设计模式。一个对象池包含一组已经初始化过且可以使用的对象,而可以在有需求时创建和销毁对象。池的用户可以从池子中取得对象,对其进行操作处理,并在不需要时归还给池子而非直接销毁它。这是一种特殊的工厂对象。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var objectPoolFactory = function (createObjFn) {
var objectPool = [];
return {
create () {
var obj = objectPool.length === 0? createObjFn.apply(this, arguments): objectPool.shift();
return obj
},
recover (obj) {
objectPool.push(obj);
}
}
};

var iframeFactory = objectPoolFactory(function () {
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);

iframe.onload = function () {
iframe.onload = null; // 防止 iframe 重复加载的bug
iframeFactory.recover(iframe); // iframe 加载完成之后回收节点
}
return iframe;
});
var iframe1 = iframeFactory.create();
iframe1.src = 'http://google.com';

var iframe2 = iframeFactory.create();
iframe1.src = 'http://facebook.com';

setTimeout(() => {
var iframe2 = iframeFactory.create();
iframe1.src = 'http://youtube.com';
}, 1000)