前言
同学们,上一章我们介绍了模板方法模式,这是一种基于继承的设计模式,通过设计抽象类可以避免类似类的重复定义,并且因为js的特殊原型克隆,我们也就js的实现方式进一步说明了模板方法模式,今天我们将进一步介绍一种新的设计模式,享元模式。
正文
享元模式的定义
享元模式是一种用于性能优化的模式,享元模式的核心是利用共享技术来有效支持大量细粒度的对象。尤其是对于javascript,浏览器所能分配的内存并不多,所以怎样节省内存就显得很有意义
举一个例子方便大家理解,假设有个衣服工厂,有50套男士衣服和50套女士衣服,这时候工厂需要生产塑料模特来穿这些衣服用于宣传,如果用程序来实现,不用享元模式的情况下,我们可能就会创建100个对象来穿这些衣服,但是随着衣服的增多,创建的对象也会越来越多,导致最后消耗的资源难以承受
换个思路想一想,如果我们不创建100个对象呢,反正男士的衣服可以用一套男士模特来试穿,而女士只需要一套女士模特来试穿,我们是不是只需要创建两个对象就行呢
这就是最简单的一个享元模式的思想,通过抽取共同点,并且用不同点来区分,尽量减少创建消耗的资源,达到一个公用的目的
内部状态与外部状态
享元模式要求将对象的属性划分为内部状态和外部状态,上一节我们说到享元模式的目标就是为了尽量减少共享对象的数量,关于怎么区分内部状态和外部状态,遵循以下原则:
- 内部状态存储于对象内部
- 内部状态可以被一些对象共享
- 内部状态独立于具体的场景,通常不会改变
- 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享
在上面的例子中,性别就是内部对象,因为我们根据性别来区别出了两种模特,而衣服类型就是外部状态,因为可以有各种各样的衣服可以选择。所以,通常来说,内部对象有多少种组合,享元模式就需要至少创建多少对象
享元模式的实现
还是之前的例子,我们先来看看享元模式直接实现这个衣服工厂应该怎么实现
这样为这100件衣服我们就创建出了100个对象,如果衣服再多一点比如1000件,谷歌火狐这些浏览器还可以勉强支持,ie浏览器直接就当场去世了,然后我们换成之前说的享元模式的方法来实现,sex作为内部属性,underwear作为外部属性
这样,我们通过享元模式只需要创建2个对象就可以实现与第一种方法相同的效果了
对象池
提了享元模式,就不得不谈一下对象池的内容了,什么是对象池呢,假设一个图书馆,里面现在有三本红宝书,有读者来借书的时候,如果还有,直接借给读者就是,如果这时候有四名读者来借红宝书,三本就不够用了,图书馆不得不去再买一本来借给第四名读者。有则取,无则造,这就是对象池。根据上面的思路我们不难实现一个通用的对象池
利用这个对象池我们可以很轻松地创建一个Model类型的对象池,来保证只要塑料模特够用,咱就不创建新的
有了这个对象池,上面的案例我们就可以这么实现
这样做的好处就是,从头到尾,其实就创建了一个模特对象而已,后面的create一直在复用之前的对象
对象池与享元模式的区别
对象池与享元模式有很大的相似处,都是复用对象,但是其实两者是有本质上的区别的。
- 池化技术中的“复用”可以理解为“重复使用”,主要目的是节省时间(比如从对象池中取一个对象,不需要重新创建)。在任意时刻,每一个对象、连接、线程,并不会被多处使用,而是被一个使用者独占,当使用完成之后,放回到池中,再由其他使用者重复利用。
- 享元模式中的“复用”可以理解为“共享使用”,在整个生命周期中,都是被所有使用者共享的,主要目的是节省空间。
我们可以很明显发现,在上面对象池的例子中,并没有分离内部状态和外部状态的过程,所以对象池与享元模式是完全不可同日而语的。
小结
这一章我们介绍了享元模式,当在业务逻辑中需要创建大量对象的时候,我们就可以考虑利用享元模式,分离这些对象的内部和外部状态,实现对象间的复用,从而减少重复资源上的消耗,我们还介绍了对象池,对象池与享元模式有相似之处,同样是为了实现对象的复用,但是与享元模式不同,在复用对象被使用的时候,是处于被独占的状态,且也没有对内部和外部状态的区分,要着重注意两者的不同