享元模式,高效的支持细粒度的对象。享元对象能做到共享的关键是区分内蕴状态和外蕴状态,一个内蕴状态是存储在享元对象内部的,并不会随环境的变化而变化,因此,一个享元可以具有内蕴状态并可以共享。一个外蕴状态是随环境变化而变化的,不可以共享。享元对象的外蕴状态必须由客户端保存,并在享元对象后被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态不能影响内蕴状态,即相互独立。
上面说了一大堆非常抽象,用gof的例子来说,例如一个文本编辑器,在上面可以编辑字母,共有52个字母,含大小写,如果它要写1000字母的文章,则有接近20倍的字母都会重复,即没写一个字母,会实例化一次字母的父类。这样就会花费很多资源,并且每一个字母都有可能带不同的样式。从这分析,我们就可以使用享元模式。首先字母就可以作为内蕴状态,因为他们是固定的52个,并且如果再次使用重复字母,我们就可以通过共享来实现,而字母的样式我们就认为它是外蕴状态,是可以随之改变的。
接下来,看看代码。
定义接口,并创建一个改变字体样式方法
public interface Flyweight {
public void operation(String state);
}
实现此接口
public class ConcerteFlyweight implements Flyweight{
//内蕴状态
private Character intrinsicState = null;
//传进来的参数属于外晕状态
public ConcerteFlyweight(Character state) {
this.intrinsicState = state;
}
public void operation(String state) {
System.out.println("intrinsicState="+intrinsicState+",Extrinsic State="+state);
}
}
内蕴状态也可认为是类内部变量,而外蕴状态则是从外部传入的参数。
接下来定义一个工厂类,因为客户端不能直接将具体的享元类实例化,而必须通过一个工厂对象,利用一个factory()得到享元对象。当客户端需要单纯的享元对象的时候,需要调用享元工厂factory()方法,并传入所需的内蕴状态,由工厂方法得到所需的享元对象。
public class FlyweightFactory {
private HashMap flies = new HashMap();
private Flyweight fly;
public FlyweightFactory(){
}
//字体样式
public Flyweight factory(Character state){
if(flies.containsKey(state)){
return (Flyweight)flies.get(state);
}else{
fly = new ConcerteFlyweight(state);
flies.put(state, fly);
return fly;
}
}
}
接下来,进行测试下
public class Client {
public static void main(String[] args){
FlyweightFactory factory=new FlyweightFactory();
Flyweight fly=factory.factory(new Character('a'));
fly.operation("罗马字符");
fly.operation("新罗马字符");
fly=factory.factory(new Character('b'));
fly.operation("阿拉伯字符");
}
}
测试结果:
intrinsicState=a,Extrinsic State=罗马字符
intrinsicState=a,Extrinsic State=新罗马字符
intrinsicState=b,Extrinsic State=阿拉伯字符
可以看到字母a共享了一次,即内蕴,字体样式不断改变,即外蕴。
以上是单纯享元,下面看看什么是复合享元。
依然是上面的例子,如果在编辑某段字母是都是同一个样式,这就可以使用复合享元。
还是同样的接口,同样的实现类
public interface Flyweight {
public void operation(String state);
}
public class ConcreteFlyweight implements Flyweight
{
private Character intrinsicState=null;
//构造子,内蕴状态作为参数传入
public ConcreteFlyweight(Character state)
{
this.intrinsicState=state;
}
//外蕴状态作为参数传入方法
public void operation(String state)
{
System.out.println("Intrinsic state="+intrinsicState+",Extrinsic State="+state);
}
}
不同的是,复合享元由单纯享元对象通过复合而成,因此提供了add()这样的聚合管理方法,由于复合享元对象具有不同的聚集元素,这些聚集元素在复合后被创建之后加入,所以这些复合享元对象是不可改变的。复合享元实现接口,并实现operation方法,这个参数对复合享元来说就是外蕴。一个复合享元对象的所有单纯享元对象元素的外蕴状态都是与复合享元对象的外蕴状态相等的,而对象所含有的单纯享元对象的内蕴状态一般不想等。
就编辑器来说,我某一段输入abac,并且都是新罗马字符,从这里理解上面的语句,就是新罗马字符即复合享元的外蕴,与每一个字母的单纯享元的外蕴是一样的,因为他们的样式都是"新罗马".而内蕴却不一定相同,因为,a,b,c是完全不同的字母。这样就理解了。
下面接着看代码:
public class ConcreteCompositeFlyweight implements Flyweight{
private HashMap flies=new HashMap(10);
private Flyweight flyweight;
public ConcreteCompositeFlyweight() { }
//增加一个新的单纯享元对象到聚集中
public void add(Character key,Flyweight fly)
{
flies.put(key,fly);
}
//外蕴状态作为参数传入到方法中
public void operation(String extrinsicState)
{
Flyweight fly=null;
for(Iterator it=flies.entrySet().iterator();it.hasNext();)
{
Map.Entry e=(Map.Entry)it.next();
fly=(Flyweight)e.getValue();
fly.operation(extrinsicState);
}
}
}
接下来看看工厂类,跟单纯享元一样,客户端不能直接调用享元对象,只能通过工厂类。
public class FlyweightFactory
{
private HashMap flies=new HashMap();
public FlyweightFactory() { }
//单纯享元工厂方法,所需状态以参量形式传入
public Flyweight factory(Character state)
{
if(flies.containsKey(state))
{
return (Flyweight)flies.get(state);
} else {
Flyweight fly=new ConcreteFlyweight(state);
flies.put(state,fly);
return fly;
}
}
//符合享元工厂方法,所需状态以参量形式传入,这个参量巧好可以使用string类型
public Flyweight factory(String compositeState)
{
ConcreteCompositeFlyweight compositeFly=new ConcreteCompositeFlyweight();
int length=compositeState.length();
Character state=null;
for(int i=0;i<length;i++)
{
state=new Character(compositeState.charAt(i));
System.out.println("factory("+state+")");
compositeFly.add(state,this.factory(state));
}
return compositeFly;
}
}
这里就是将复合享元对象,分解成若干个单纯享元对象,进行处理。
最后我们测试一下
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
FlyweightFactory factory=new FlyweightFactory();
Flyweight fly=(Flyweight) factory.factory("abac");
fly.operation("罗马字符");
}
}
测试结果:
factory(a)
factory(b)
factory(a)
factory(c)
Intrinsic state=a,Extrinsic State=罗马字符
Intrinsic state=c,Extrinsic State=罗马字符
Intrinsic state=b,Extrinsic State=罗马字符
这就说明了,将复合享元对象差分为若干个单纯享元对象处理,并且外蕴是相同的。