享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
一、定义
运用共享技术有效地支持大量细粒度的对象。
二、解决的问题
在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
三、模式中的角色
内蕴状态存储在享元内部,不会随环境的改变而有所不同,是可以共享的;外蕴状态是不可以共享的,它随环境的改变而改变的,因此外蕴状态是由客户端来保持(因为环境的变化是由客户端引起的)。在每个具体的环境下,客户端将外蕴状态传递给享元,从而创建不同的对象出来。享元模式可分为:单纯享元模式和复合享元模式。
- 抽象享元角色(Flyweight):为具体享元角色规定了必须实现的方法,而外蕴状态就是以参数的形式通过此方法传入。在Java中可以由抽象类、接口来担当。
- 具体享元角色(ConcreteFlyweight):实现抽象角色规定的方法。如果存在内蕴状态,就负责为内蕴状态提供存储空间。
- 复合享元角色(ConcreteCompositeFlyweight):它所代表的对象是不可以共享的,并且可以分解成为多个单纯享元对象的组合。
- 享元工厂角色(FlyweightFactory):负责创建和管理享元角色。要想达到共享的目的,这个角色的实现是关键!
- 客户端角色(client):维护对所有享元对象的引用,而且还需要存储对应的外蕴状态。
四、模式解读
左半部,和简单工厂模式类似;再看右半部,像合成模式,合成模式用在此处就是为了将具体享元角色和复合享元角色同等对待和处理,通过将享元模式与合成模式组合在一起,可以确保复合享元中所包含的每个单纯享元都具有相同的外蕴状态,而这些单纯享元的内蕴状态往往是不同的。
五、JDK涉及到的设计模式
JDK中体现有Integer.valueOf(inti),Character.valueOf(char c)以及String常量池。
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
/* i 在IntegerCache中,返回共享对象,不在其中创建新的Integer对象 */
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
/* IntegerCache,一个内部类,注意它的属性都是定义为static final */
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
/**
* h值,可以通过设置jdk的AutoBoxCacheMax参数调整,
* 自动缓存区间设置为[-128,N],注意区间的下界是固定
*/
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// 数组大小最大为Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// 不能解析为int,忽略
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++); //-128到high值逐一分配到缓存数组
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
六、模式总结
优缺点
享元模式优点:
- 它能够大幅度的降低内存中对象的数量;
享元模式缺点:
- 它使得系统逻辑复杂化,而且在一定程度上外蕴状态影响了系统的速度。
适用场景
- 当我们发现某个类型的对象有大量的实例时,我们是否可以对这些实例进行分类,经过分类后,我们发现只有很少的类别的情况下。
- 我们发现通过使用享元模式后能够提高系统的性能和不会带来更多的复杂度时。
- 享元模式一般是给出本地内存资源节省的一个方案,并不适合互联网上的分布式应用的情况,不过享元模式对于排他性的要求资源的控制,是个不错的选择的。