设计模式之享元模式
享元模式(FlyWeight),其含义是运用共享技术有效地支持大量细粒度的对象,即系统只使用少量的对象,而这些对象都相近,状态变化很小,对象使用次数却很多。
享元模式能做到共享的关键是区分内蕴状态和外蕴状态,一个内蕴状态是存储在享元对象内部的,并且是不会随环境改变而有所不同的。因此一个享元可以具有内蕴状态并可以共享。一个外蕴状态是随环境改变而改变的、不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态不可以影响享元对象的内蕴对象。换句话说,它们是互相独立的。
其适用性:
一个应用程序使用了大量的对象,
完全由于使用大量的对象,造成很大的存储开销,
对象的大多数状态都可变为外部状态,
如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,
应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
结构如下:
享元模式在使用时分为单纯享元模式和复合享元模式两种,单纯享元模式中Flyweight都是可共享的,也是现在Flyweight实现的较多实例。现在先列出单纯享元模式:
Flyweight.java
package org.designpattern.structural.flyweight;
protected Character intrinsicState;
protected Flyweight(Character intrinsicState){
this.intrinsicState = intrinsicState;
};
public abstract void operation(String extrinsicState);
public Character getIntrinsicState(){
return this.intrinsicState;
};
}
ConcreteFlyweight.java继承了Flyweight,实现了一个operation方法,类略。Flyweight是通过工厂得到的,下面是FlyweightFactory.java
import java.util.HashMap;
public class FlyweightFactory {
private HashMap flyWeights;
public FlyweightFactory(){
flyWeights = new HashMap();
}
public Flyweight getFlyweight(Character key){
if(this.flyWeights.containsKey(key)){
System.out.println(key + " already created!");
return (Flyweight) flyWeights.get(key);
}else{
Flyweight flyweight = new ConcreteFlyweight(key);
flyWeights.put(key,flyweight);
return flyweight;
}
}
}
如果是单纯享元模式,到这里再加上客户测试类就结束了。如果使用复合享元模式,复合元素的对象的状态是会改变的,所以复合享元对象是不能共享的。需要再增加一个类UnsharedConcreteFlyeweight.java
public class UnsharedConcreteFlyweight extends Flyweight {
private Flyweight flyweight;
public UnsharedConcreteFlyweight(Character intrinsicState) {
super(intrinsicState);
this.unsharedFlyweights = new HashMap(20);
}
public void add(Character key,Flyweight flyweight){
this.unsharedFlyweights.put(key,flyweight);
}
@Override
public void operation(String extrinsicState) {
System.out.println("extrinsicState:" + extrinsicState);
Iterator<Map.Entry<Character,Flyweight>> it = unsharedFlyweights.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Character,Flyweight> entry = it.next();
Flyweight fly = entry.getValue();
fly.operation(extrinsicState);
}
}
}
此时在FlyweighFactory.java中需要增加一个方法支持不可共享的Flyweight的调用:
public Flyweight getUnsharedFlyweight(String keys ){
for(int i=0;i<keys.length();i++){
Character character = new Character(keys.charAt(i));
unsharedConcreteFlyweight.add(character,getFlyweight(character));
}
return unsharedConcreteFlyweight;
}
下面是客户测试类:
public class Main {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight('a');
fly1.operation("First call!");
Flyweight fly2 = factory.getFlyweight('b');
fly2.operation("Second call!");
Flyweight fly3 = factory.getFlyweight('a');
fly3.operation("Third call!");
System.out.println(fly1 == fly3);
Flyweight unsharedFlyweight = factory.getUnsharedFlyweight("abcde");
unsharedFlyweight.operation("Forth call!");
}
}
享元模式的有效性很大程度上取决于在何种情景下使用它,其主要缺点是使系统设计变得更加复杂,将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。我们通常会把Flyweight工厂设计为单例模式,其可以和许多模式共同使用更有效。