设计模式——享元模式
在程序设计中,有时候需要生产大量细粒度的对象来表示数据,如果能发现这些对象中除了部分特殊参数外其他开销基本相同,就可以通过享元模式把特殊参数移动到外面,在创建对象时再传进来,大幅度减少需要实例化的对象数量。这是一种优化性能的设计模式。
内部状态和外部状态
由于享元模式的目标是减少对象数量,实际上就是先创建一个原始对象,然后根据不同的环境传入不同参数。原始对象内的参数就称为内部状态,外部参数就称为外部状态。使用享元模式的关键就是如何区分内部状态和外部状态。下面几点可以作为参考:
- 内部状态可以被一些对象共享
- 内部状态独立于具体场景
- 外部状态不可以被共享
- 外部状态取决于具体场景
具体实例
现在假设有一家咖啡店,可以定义咖啡的味道和数量,并送到顾客的就坐位置。常规写法如下:
let CoffeeOrder = function(id, flavor, table, num) {
this.id = id;
this.flavor = flavor;
this.table = table;
this.num = num;
};
CoffeeOrder.prototype.serving = function() {
console.log(`送到${this.table}号台的${this.flavor}共${this.num}杯`);
}
let id = 0;
let startServing = function(orders) {
for (let i = 0, order; order = orders[i++];) {
let coffeeOrder = new CoffeeOrder(id++, order.flavor, order.table, order.num);
coffeeOrder.serving();
};
startServing([
{
flavor:...,
table:...,
num:...
}
// ..其他参数
])
}
可以看到没有优化的情况下,会为每个订单创建一个对象。刚开始可能没问题,但随着订单增加系统就会越来越慢。利用享元模式,我们就可以将flavor分类为内部状态,其他的参数都为外部状态。
享元模式重构
内部状态我们通常采用工厂模式来管理。首先检查之前是否创建了这个内部状态对象,如果有就返回,没有就重新创建并存储到对象池里便于管理。
let Coffee = function(flavor) {
this.flavor = flavor;
};
Coffee.prototype.serving = function(id) {
orderManager.setExternalState(id, this);
console.log(`送到${this.table}号台的${this.flavor}共${this.num}杯`);
};
let CoffeeFactory = (function() {
let existingCoffee = {};
return {
createCoffee: function(flavor) {
if (existingCoffee[flavor]) {
return existingCoffee[flavor];
}
return existingCoffee[flavor] = new Coffee(flavor);
}
}
})();
外部状态我们通过一个管理对象orderManger来管理,它负责向内部状态对象提交具体对象创建的请求,并用一个Datebase对象保存所有具体对象的外部状态以便完善具体对象的全部状态。代码如下:
let orderManager = (function() {
let orderDatabase = {}; // 保存外部状态
return {
add: function(id, flavor, table, num) { // 存入外部状态数据并提交具体对象创建请求
let flyWeightObj = CoffeeFactory.createCoffee(flavor);
orderDatabase[id] = {
table,
num
};
return flyWeightObj;
},
setExernalState: function(id, flyWeightObj) { // 提取出具体数据
let orderData = orderDatabase[id];
for (let i in orderData) {
flyWeightObj[i] =orderData[i];
}
}
}
})();
执行方法如下:
let id = 0;
let startServing = function(orders) {
for (let i = 0, order; order = orders[i++];) {
let coffeeOrder = orderManager.add(id, order.flavor, order.table, order.num);
coffeeOrder.serving(id++);
}
};
startServing([
{
flavor: '...',
table: ...,
num: 1
},
// ..其他参数
]);
小结
享元模式主要用来解决性能问题,优点在于大量减少内存里的对象数量,通过分离内部状态使得具体对象之间可以共享。如果一个业务里使用了大量对象并造成很大存储开销时就可以考虑享元模式是否适用。
-- EOF --