享元模式(flyweight)是一种用于性能优化的模式,之所以用“fly”其意为蝇量级。而享元模式的核心就是运用共享技术来有效支持大量细粒度的对象。虽然面向对象可以非常方便的解决一些扩展性的问题,但是在这个过程中系统势必会产生一些类或者对象,如果系统中存在对象的个数过多时,将会导致系统的性能下降。对于这样的问题解决最简单直接的办法就是减少系统中对象的个数。在javascript中,浏览器特别是移动端的浏览器分配的内存并不多,在这种情况下,节省内存就变得十分重要了。

享元模式的定义

所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。
享元模式要求将对象的属性划分为内部状态和外部状态,所以在了解享元模式之前我们先要了解两个概念:内部状态、外部状态。

内部状态

在享元对象内部不随外界环境改变而改变的共享部分。
内部属性通常来说应该具备一下几条特性。

  1. 内部状态存储于对象内部。

  2. 内部状态可以被一些对象共享。

  3. 内部状态独立于具体的场景,通常不会改变。

外部状态

外部状态取决于具体的业务场景,并根据场景而变化,不可以共享的状态。一个外部状态与另一个外部状态之间是相互独立的。

由于享元模式区分了内部状态和外部状态,所以我们可以通过设置不同的外部状态使得相同的对象可以具备一些不同的特性,而内部状态设置为相同部分。在我们的程序设计过程中,我们可能会需要大量的细粒度对象来表示对象,如果这些对象除了几个参数不同外其他部分都相同,这个时候我们就可以利用享元模式来大大减少应用程序当中的对象。如何利用享元模式呢?这里我们只需要将他们少部分的不同的部分当做参数移动到类实例的外部去,然后再方法调用的时候将他们传递过来就可以了。

享元模式的使用

假设我们要生产n辆汽车,我们在不用享元模式的情况下,可能会这样

function Car(wheel, price,seat, color, logo) {
    this.wheel = wheel
    this.price = price
    this.seat = seat
    this.color = color
    this.brand = logo
}

然后我们就可以为所欲为了。

var car 1 = new Car(x, x, x, x, x)
var car 2 = new Car(x, x, x, x, x)
var car 3 = new Car(x, x, x, x, x)
...............................
var car 1000 = new Car(x, x, x, x, x)

当我们对象实例化次数过多,就会导致内存无限制的增长,性能可想而知。那么怎么样避免这样的情况出现呢。这里就要用到了享元模式。

var flyWight  = (function(){
    // 这里是存储器,用存储所有的car对象
    var Cars = {}
    var Car = function(d) {
        this.wheel = d.wheel
        this.price = d.price
        this.color = d.color
        this.seat = d.seat
    }
    // 以logo(品牌)为类构建Car类别
    var _factory = function(d) {
        if(!Cars[d.logo]) {
            Cars[d.logo] = new Car(d)
        }
        //返回该品牌的类别
        return Cars[d.logo]
    }

    var carItem =  {
        allCars : {},
        //添加一辆车
        addCar : function(data) {
            if(this.allCars[data.id]) return this.allCars[data.id]
            this.allCars[data.id] = {
                id : data.id,
                logo : data.logo,
                car : _factory(data)
            }
        },
    }
    return carItem
})()

var data = [
    { id: 1, wheel: 4, price: 50000, color: 'red', logo: 'benz', seat: 8 },
    { id: 2, wheel: 4, price: 40000, color: 'blue', logo: 'bmw', seat: 4 },
    { id: 3, wheel: 4, price: 50000, color: 'red', logo: 'ford', seat: 2 },
]
for(var i = 0; i < data.length; i++) {
    flyWight.addCar(data[i])
}

从以上的代码我们可以看到,利用汽车的品牌作为实例的对象而不是单一的某个汽车。因为品牌再多是不会超过汽车的总数的,所以我们实际上要实例的对象就是十几个甚至是几个对象而已。
享元模式是为解决性能问题而生的模式,是很好的性能优化方案,但是很明显它也会带来一些复杂性的问题。一般情况下在这四种情况下应该考虑使用享元模式。

  1. 一个程序中使用了大量的相似对象

  2. 由于使用大量的对象造成很大的内存开销

  3. 对象的大多数状态都可以变为外部状态

  4. 可以用相对较少的对象取代大量对象


wupengyu
1.8k 声望166 粉丝

写作是为了更好的思考