什么是享元模式
享元模式是一种用于性能优化的模式,享元模式的核心是运用共享技术来有效支持大量细粒度的对象。
如果系统中因为创建了大量类似的对象而导致内存占用过高,享元模式就非常有用了。
享元模式要求将对象的属性划分为内部属性与外部属性。享元模式的目标是尽量减少共享对象的数量,关于如何划分内部状态和外部状态,有以下几点建议:
- 内部属性存储于对象内部。
- 内部属性可以被一些对象共享。
- 内部属性独立于具体的场景,通常不会改变。
- 外部属性取决于具体的场景,并根据场景而变化,外部属性不能被共享。
把所有内部属性相同的对象都指定为同一个共享的对象。而外部属性可以从对象身上剥离出来,并储存在外部。剥离了外部属性的对象成为共享对象,外部属性在必要时被传入共享对象来组装成一个完整的对象。虽然组装外部属性成为一个完整对象的过程需要花费一定的时间,但却可以大大减少系统中的对象数量,相比之下,这点时间或许是微不足道的。因此,享元模式是一种用时间换空间的优化模式。
实现一个享元模式
假设有个服装厂,目前的产品有 50 种男式外套和 50 种女士外套,为了推销产品,工厂决定生产一些塑料模特来穿上他们的外套拍成广告照片。 正常情况下需要 50 个男模特和 50 个女模特,然后让他们每人分别穿上一件外套来拍照。不使用享元模式的情况下,在程序里也许会这样实现:
class Model {
constructor(sex, looseCoat) {
this.sex = sex;
this.looseCoat = looseCoat;
}
takePhoto() {
console.log('sex= ' + this.sex + ' looseCoat=' + this.looseCoat);
}
}
for (let i = 1; i <= 50; i++) {
const maleModel = new Model('male', 'looseCoat' + i);
maleModel.takePhoto();
};
for (let j = 1; j <= 50; j++) {
const femaleModel = new Model('female', 'looseCoat' + j);
femaleModel.takePhoto();
};
按照上面的程序运行,那50种男式外套和50种女生外套,将会产生100个模特。假如有1万种男式外套和1万种女生外套,那不是需要1万个模特,那这个程序可能会因为存在如此多的对象(模特)已经提前崩溃。然而现实中不会有这么傻的工厂。只需要1个男模特和1个女模特。
那么程序应该这么优化。
Model只需要只接收sex参数,只需要区别男女模特即可。
class Model {
constructor(sex) {
this.sex = sex;
}
takePhoto() {
console.log('sex= ' + this.sex + ' looseCoat=' + this.looseCoat);
}
}
分别创建一个男模特对象和一个女模特对象:
const maleModel = new Model('male');
const femaleModel = new Model('female');
给男模特依次穿上所有的男装,并进行拍照:
for (let i = 1; i <= 50; i++) {
maleModel.looseCoat = 'looseCoat' + i;
maleModel.takePhoto();
};
同样,给女模特依次穿上所有的女装,并进行拍照:
for (var j = 1; j <= 50; j++) {
femaleModel.looseCoat = 'looseCoat' + j;
femaleModel.takePhoto();
};
以上就是一个非常经典的享元模式。
把模特的性别sex当作模特这个对象的内部属性,把模特身上的外套looseCoat当作外部属性。把外套looseCoat属性剥离后,模特这个对象就可以被共享,然后再必要的时候给模特附上外套looseCoat属性,组成一个完整的对象。
什么时候使用享元模式
享元模式带来的好处很大程度上取决于如何使用以及何时使用,一般来说,以下情况发生时便可以使用享元模式。
- 一个程序中使用了大量的相似对象。
- 由于使用了大量对象,造成很大的内存开销。
- 对象的大多数状态都可以变为外部状态。
- 剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。