假设有个内衣工厂,目前的产品有 50种男式内衣和50种女士内衣,为了推销产品,工厂决定生产一些塑料模特来穿上他们的内衣拍成广告照片。正常情况下需要50个男模特和 50 个女模特,然后让他们每人分别穿上一件内衣来拍照,这就需要100个模特。如果使用共享模式,只需要男女两个模特,然后让他们可以分别穿上不同的内衣来拍照
var Model = function(sex) {
this.sex = sex; // 性别是内部状态
};
Model.prototype.takePhoto = function() {
console.log('sex= ' + this.sex + ' underwear=' + this.underwear);
};
var maleModel = new Model('male'),
femaleModel = new Model('female');
for (var i = 1; i <= 50; i++) {
maleModel.underwear = 'underwear' + i; // 衣服是外部状态
maleModel.takePhoto();
};
for (var j = 1; j <= 50; j++) {
femaleModel.underwear = 'underwear' + j;
femaleModel.takePhoto();
};
享元模式的关键是如何区别内部状态和外部状态,可以被对象共享的属性通常被划分为内部状态,模特的性别就可以作为内部状态储存在共享对象的内部。外部状态取决于具体的场景,并根据场景而变化,就像例子中每件衣服都是不同的,它们不能被一些对象共享,因此只能被划分为外部状态。
回收池
对象池维护一个装载空闲对象的池子,如果需要对象的时候,不是直接 new,而是转从对象池里获取。如果对象池里没有空闲对象,则创建一个新的对象,当获取出的对象完成它的职责之后, 再进入池子等待被下次获取。
在 Web前端开发中,对象池使用最多的场景大概就是跟 DOM 有关的操作。很多空间和时间都消耗在了 DOM节点上,如何避免频繁地创建和删除 DOM节点就成了一个有意义的话题。对象池技术的应用非常广泛,HTTP连接池和数据库连接池都是其代表应用。
假设我们在开发一个地图应用,地图上经常会出现一些标志地名的小气泡,我们叫它toolTip。
当搜索附近超市时,地图上出现了6个气泡,按照对象池的思想,在第二次搜索开始之前,并不会把第一次创建的2个小气泡删除掉,而是把它们放进对象池。这样在第二次的搜索结果页面里,我们只需要再创建 4个小气泡而不是 6个
var toolTipFactory = (function() {
var toolTipPool = []; // toolTip 对象池
return {
create: function() {
if (toolTipPool.length === 0) {
var div = document.createElement('div'); // 创建一个 dom
document.body.appendChild(div);
return div;
} else {
return toolTipPool.shift(); // 则从对象池中取出一个 dom
}
},
recover: function(tooltipDom) {
return toolTipPool.push(tooltipDom); // 对象池回收 dom
}
}
})();
var ary = [];
// 创建两个气泡
for (var i = 0, str; str = ['A', 'B'][i++];) {
var toolTip = toolTipFactory.create();
toolTip.innerHTML = str;
ary.push(toolTip);
};
// 把气泡添加到回收池
for (var i = 0, toolTip; toolTip = ary[i++];) {
toolTipFactory.recover(toolTip);
};
// 前两次循环从回收池取气泡,后四次创建新气泡
for (var i = 0, str; str = ['A', 'B', 'C', 'D', 'E', 'F'][i++];) {
var toolTip = toolTipFactory.create();
toolTip.innerHTML = str;
ary.push(toolTip);
};