让我们先来复习下java中String类型的特性:String类型的对象一旦被创造就不可改变;当两个String对象所包含的内容相同的时候,JVM只创建一个String对象对应这两个不同的对象引用。让我们来证实下着两个特性吧(如果你已经了解,请跳过直接阅读第二部分)。
先来验证下第二个特性:
public class TestPattern {
public static void main(String[] args){
String n = "I Love Java";
String m = "I Love Java";
System.out.println(n==m);
}
}
这段代码会告诉你n==m是true,这就说明了在JVM中n和m两个引用了同一个String对象(如果你还分不清== 和 equals的区别的话,请先确认)。
那么接着验证下第一个特性:
在系统输出之前加入一行代码“m = m + "hehe";”,这时候n==m结果为false,为什么刚才两个还是引用相同的对象,现在就不是了呢?原因就是在执行后添加语句时,m指向了一个新创建的String对象,而不是修改引用的对象。
呵呵,说着说着就差点跑了题,并不是每个String的特性都跟我们今天的主题有关的。
String类型的设计避免了在创建N多的String对象时产生的不必要的资源损耗,可以说是享元模式应用的范例,那么让我们带着对享元的一点模糊的认识开始,来看看怎么在自己的程序中正确的使用享元模式!
享元模式:运用共享技术有效地支持大量大量细粒度的对象.
享元模式英文称为“Flyweight Pattern”,我非常感谢将Flyweight Pattern翻译成享元模式的那位强人,因为这个词将这个模式使用的方式明白得表示了出来;如果翻译成为羽量级模式或者蝇量级模式等等,虽然可以含蓄的表现出使用此模式达到的目的,但是还是没有抓住此模式的关键。
享元模式的定义为:采用一个共享来避免大量拥有相同内容对象的开销。这种开销中最常见、直观的就是内存的损耗。享元模式以共享的方式高效的支持大量的细粒度对象。
在名字和定义中都体现出了共享这一个核心概念,那么怎么来实现共享呢?要知道每个事物都是不同的,但是又有一定的共性,如果只有完全相同的事物才能共享,那么享元模式可以说就是不可行的;因此我们应该尽量将事物的共性共享,而又保留它的个性。为了做到这点,享元模式中区分了内蕴状态和外蕴状态。内蕴状态就是共性,外蕴状态就是个性了。
注:共享的对象必须是不可变的,不然一变则全变(如果有这种需求除外)。
内蕴状态存储在享元内部,不会随环境的改变而有所不同,是可以共享的;外蕴状态是不可以共享的,它随环境的改变而改变的,因此外蕴状态是由客户端来保持(因为环境的变化是由客户端引起的)。在每个具体的环境下,客户端将外蕴状态传递给享元,从而创建不同的对象出来。至于怎样来维护客户端保持的外蕴状态和享元内部保持的内蕴状态的对应关系,你先不用担心这个问题,我们后面会涉及到的。
我们引用《Java与模式》中的分类,将享元模式分为:单纯享元模式和复合享元模式。在下一个小节里面我们将详细的讲解这两种享元模式。
享元模式的组成部份:
1.享元接口 定义了享元
2.具体享远 实现享元接口
3.享元工厂 负责管理和创建享元对象,一般包含一个Map, 保存享元对象,使用都请求享元时,先到map里找是否有这个享元,如果有,返回,没有就创建该享元.机制有点像对时所做的简单的缓存.
各部份的组成关系:
使用优缺点
享元模式优点就在于它能够大幅度的降低内存中对象的数量;而为了做到这一步也带来了它的缺点:它使得系统逻辑复杂化,而且在一定程度上外蕴状态影响了系统的速度。
所以一定要切记使用享元模式的条件:
1) 系统中有大量的对象,他们使系统的效率降低。
2) 这些对象的状态可以分离出所需要的内外两部分。
外蕴状态和内蕴状态的划分以及两者关系的对应也是非常值得重视的。只有将内外划分妥当才能使内蕴状态发挥它应有的作用;如果划分失误,在最糟糕的情况下系统中的对象是一个也不会减少的!两者的对应关系的维护和查找也是要花费一定的空间(当然这个比起不使用共享对象要小得多)和时间的,可以说享元模式就是使用时间来换取空间的。在Gof的书中是使用了B树来进行对应关系查找优化。
总结
也许你要长叹一声:这个享元模式未必太复杂了吧!这点是不得不承认的,也许由于它的复杂,实际应用也不是很多,这是我们更加无法看清他的真面目了。不过享元模式并不是鸡肋,它的精髓——共享是对我们系统优化非常有好处的,而且这种思想已经别越来越多的应用,这应该就算是享元模式的应用了吧。如果你已经领会到了享元模式的精髓,那么也就是掌握了享元模式了!
匆匆学完了享元模式,不知道理解上有没有纰漏,希望大家能指正出来,一起共同进步!其实我一直想使用一个实际系统中或者实践中的例子来讲解享元模式,可是毕竟自己的工作经验太少了!!于是想在网上找一些灵感来,可是狂搜一阵子也没有发现什么,于是就又落俗套的使用了一个比喻的例子。如果您对此深有体会的话,还烦请不吝赐教!!