设计模式(结构型设计模式——享元模式)
享元模式
基本定义
- 享元模式就是运行共享技术有效地支持大量细粒度(对象相似度较高的属性)对对象的复用,系统使用少量的对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。
- 享元模式是支持大量细粒度对象的复用,所以享元模式要求被共享的对象必须是细粒度对象。
- 享元模式的两个概念:内部状态,外部状态
- 内部状态: 在享元对象内部不随外界环境改变而改变的共享部分
- 外部状态:随着环境的变化而变化,不能共享的状态就是外部状态
- 由于享元模式区分了内部状态和外部状态,所以我们可以《通过设置不同的外部状态》使得相同的对象可以具备一些不同的特性,《内部状态存储于享元对象内部,而外部状态则应该由客户端来考虑》
模式结构
Flyweight: 抽象享元类,所有具体享元类的超类或接口,通过这个接口可以接受并作用于外部专题
ConcreteFlyweight: 具体的享元类,指定内部状态,为内部状态增加存储空间
UnsharedConcreteFlyweight: 非共享具体享元类,指出哪些不需要共享的Flyweight子类
FlyweightFactory:开辟静态存储,用来创建并管理flyweight对象,它主要用于确保合理地共享Flyweight,当用户请求一个Flyweight的时候,FlyweightFactotry就会提供一个已经创建的Flyweight对象或者新建一个(如果不存在)
优点
享元模式的优点在于她能够极大的减少系统中对象的个数
享元模式由于使用了外部状态,外部状态相对独立,不会影响内部状态,所以享元对象能够在不同的环境共享
缺点
由于享元模式区分内外部状态,使的程序更加复杂了
为了使对象共享,享元模式需要将享元对象状态外部化,而读取外部状态使得运行时间变长
使用场景
如果一个系统中存在大量的相同或者相似的对象,由于这类对象的大量使用,会造成系统内存耗费,可以使用享元模式来减少系统中对象的数量
对象大部分状态都可以外部化,可以将这些外部状态传入对象中
总结
享元模式的优点在于她能够极大的减少系统中对象的数量,但它可能会引起系统逻辑更加复杂化
享元模式的核心在于享元工厂,它主要用来确保合理地共享享元对象
内部状态为不共享部分,储存于享元对象内部,而外部状态是可变部分,它应当由客户端负责
JDK源码
//测试类
public class IntegerTest {
public static void main(String[] args) {
Integer a = Integer.valueOf(127);
Integer b = Integer.valueOf(127);
System.out.println("a == b : " + (a == b));//输出true
Integer c = Integer.valueOf(128);
Integer d = Integer.valueOf(128);
System.out.println("c == d : " + (c == d)); //输出false
}
}
//源码
public final class Integer extends Number implements Comparable<Integer> {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
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);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
}