享元模式(Flyweight Pattern)
概念
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享大量细粒度对象来减少内存占用。享元模式将内外部状态分离,内部状态可以共享,外部状态由客户端独立维护,从而避免重复创建相同内容的对象。
应用场景
-
需要大量相似对象且内存消耗较大:当系统需要生成大量相似的对象,而这些对象中的大部分内容是相同或可以共享的,通过享元模式可以减少内存开销。例如,绘图程序中的文字对象、图形对象等。
-
对象的部分状态是可共享的:如果一个对象的某些部分是可变的(外部状态),而其他部分是不可变的(内部状态),那么可以使用享元模式,将内部状态共享,外部状态由客户端独立处理。例如,在棋类游戏中,棋子的颜色和形状是共享的,而位置则是独立的。
-
系统中创建了大量细粒度对象:当系统中需要创建大量的细粒度对象时,可以使用享元模式来减少对象的数量,从而节省资源。
注意点
- 区分内外部状态:内部状态是对象可以共享的部分,外部状态是依赖于具体场景并且不可共享的部分。外部状态在使用时会被传递给享元对象。
- 使用享元工厂管理享元对象:由于共享对象的核心是避免重复创建,工厂模式通常与享元模式配合使用,用来管理享元对象的创建和共享。
- 节省内存的代价是增加了系统的复杂性:享元模式虽然可以减少对象的数量,但增加了状态管理的复杂性,外部状态需要由客户端管理。
核心要素
- Flyweight(享元接口):定义享元对象接口,通常包含操作内部状态的方法。
- ConcreteFlyweight(具体享元类):实现享元接口,封装可共享的内部状态。
- UnsharedConcreteFlyweight(非共享享元类):有时享元对象的某些实例不能共享,此时可以使用这个类表示非共享的对象。
- FlyweightFactory(享元工厂类):负责管理和创建享元对象,确保对象可以被共享。
Java代码完整示例
java
// 享元接口
interface Flyweight {
void operation(String externalState); // 接受外部状态
}
// 具体享元类,保存共享的内部状态
class ConcreteFlyweight implements Flyweight {
private String internalState; // 内部状态
public ConcreteFlyweight(String internalState) {
this.internalState = internalState;
}
@Override
public void operation(String externalState) {
System.out.println("内部状态: " + internalState + ", 外部状态: " + externalState);
}
}
// 享元工厂类,管理享元对象
class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String internalState) {
// 检查享元对象是否已经存在
if (!flyweights.containsKey(internalState)) {
flyweights.put(internalState, new ConcreteFlyweight(internalState));
System.out.println("创建享元对象,内部状态: " + internalState);
}
return flyweights.get(internalState);
}
public int getFlyweightCount() {
return flyweights.size();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
// 创建享元对象并共享内部状态
Flyweight flyweight1 = factory.getFlyweight("A");
flyweight1.operation("X");
Flyweight flyweight2 = factory.getFlyweight("B");
flyweight2.operation("Y");
Flyweight flyweight3 = factory.getFlyweight("A");
flyweight3.operation("Z");
System.out.println("享元对象的数量: " + factory.getFlyweightCount());
}
}
输出结果:
创建享元对象,内部状态: A
内部状态: A, 外部状态: X
创建享元对象,内部状态: B
内部状态: B, 外部状态: Y
内部状态: A, 外部状态: Z
享元对象的数量: 2
各种变形用法完整示例
-
非共享的享元对象
有时某些享元对象不能共享,需要为这些对象设计一个独立的类,这类对象称为"非共享享元对象"。例如,在某些系统中,特殊状态的对象不能被共享。
代码示例:
java// 非共享享元类 class UnsharedConcreteFlyweight implements Flyweight { private String allState; public UnsharedConcreteFlyweight(String allState) { this.allState = allState; } @Override public void operation(String externalState) { System.out.println("非共享对象状态: " + allState + ", 外部状态: " + externalState); } } public class ClientWithUnsharedFlyweight { public static void main(String[] args) { FlyweightFactory factory = new FlyweightFactory(); Flyweight flyweight1 = factory.getFlyweight("A"); flyweight1.operation("X"); Flyweight unsharedFlyweight = new UnsharedConcreteFlyweight("Special"); unsharedFlyweight.operation("Y"); System.out.println("享元对象的数量: " + factory.getFlyweightCount()); } }
-
复合享元模式
复合享元模式是指将多个享元对象组合起来形成一个更大的享元对象,外部状态被管理成一个组合结构。这个组合结构可以递归地包含多个享元对象。
代码示例:
java// 复合享元类 class CompositeFlyweight implements Flyweight { private Map<String, Flyweight> flyweightMap = new HashMap<>(); public void add(String key, Flyweight flyweight) { flyweightMap.put(key, flyweight); } @Override public void operation(String externalState) { for (Map.Entry<String, Flyweight> entry : flyweightMap.entrySet()) { entry.getValue().operation(externalState); } } } public class ClientCompositeFlyweight { public static void main(String[] args) { FlyweightFactory factory = new FlyweightFactory(); // 创建单个享元对象 Flyweight flyweightA = factory.getFlyweight("A"); Flyweight flyweightB = factory.getFlyweight("B"); // 创建复合享元对象 CompositeFlyweight compositeFlyweight = new CompositeFlyweight(); compositeFlyweight.add("A", flyweightA); compositeFlyweight.add("B", flyweightB); // 使用复合享元 compositeFlyweight.operation("CompositeState"); System.out.println("享元对象的数量: " + factory.getFlyweightCount()); } }
-
与工厂模式结合
享元模式通常与工厂模式结合使用,由工厂类负责管理和创建享元对象,确保对象可以被重复使用。工厂类通常通过缓存已创建的对象来避免重复创建。
代码示例:
java// 在前面的 FlyweightFactory 中已经使用了工厂模式
-
享元模式在Java标准库中的应用
在Java的标准库中,
Integer.valueOf()
方法就是享元模式的一个实际应用。为了减少内存消耗,JDK对-128
到127
之间的整数进行了缓存。代码示例:
javapublic class FlyweightInJava { public static void main(String[] args) { Integer int1 = Integer.valueOf(100); Integer int2 = Integer.valueOf(100); // 比较对象引用 System.out.println(int1 == int2); // true Integer int3 = Integer.valueOf(200); Integer int4 = Integer.valueOf(200); System.out.println(int3 == int4); // false } }
总结
享元模式是一种优化内存使用的有效方式,尤其适用于需要创建大量细粒度对象的系统。通过共享内部状态,享元模式可以减少重复对象的创建,从而降低内存开销。如果系统中对象数量巨大且状态重复较多,享元模式可以显著提升性能和内存使用效率。