一、 享元(Flyweight)模式的结构
享元模式是对象的结构模式,享元模式以共享的方式高效地支持大量的细粒度对象。
享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。
一个内蕴状态是存储在享元对象内部的,并且是不会随环境改变而有所不同的。因此,一个享元可以具有内蕴状态并可以共享。
一个外蕴状态是随环境改变而改变的、不可共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。
外蕴状态不可以影响享元对象的内蕴状态。换句话说,它们是相互独立的。
享元模式可分为单纯享元模式和复合享元模式。
单纯享元模式所下图所示:
复合享元模式的结构:
二、 享元模式的实现
单纯享元模式
public abstract class Flyweight {
abstract public void operation(String state);
}
/**
* 享元对象有一个内蕴状态,以一个Character类型的intrinsicState属性代表
* 它的值应当在享元对象被创建时赋予。所有的内蕴状态在对象创建之后,就不会再改变。
*如果享元对象有外蕴状态的话,所有的外部状态都必须存储在客户端,在使用享元对象时,
*再由客户端传入享元对象。
*/
public class ConcreteFlyweight extends Flyweight {
private Character intrinsicState = null;
public ConcreteFlyweight(Character state){
this.intrinsicState = state;
}
@Override
//外蕴状态作为参量传入方法中,改变方法的行为,但并不改变对象的内蕴状态
public void operation(String state) {
System.out.println("/nIntrinsic State = "
+intrinsicState+",Extrinsic State" + state);
}
}
/**
* 客户端不可以直接将具体享元类实例化,而必须通过一个工厂对象,
* 利用一个factory()方法得到享元对象。一般而言,享元工厂对象在整个系统中只有一个
* 因此可以使用单例模式。
*/
public class FlyweightFactory {
private HashMap flies = new HashMap();
private Flyweight lnkFlyweight;
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;
}
}
}
复合享元模式的实现
这里只列出ConcreteCompositeFlyweight的代码:
/**
* 1、复合享元对象是由单纯的享元对象通过复合而成,因此它提供了add()这样的聚集管理方法。
* 由于一个复合享元对象具有不同的聚集元素,这些聚集元素在复合享元对象被创建之后加入,
* 这本身就意味着复合享元对象的状态是会改变的,因此复合享元对象是不能共享的。
* 2、复合享元角色实现了抽象享元角色所规定的接口,也就是operation方法,这个方法有一个参量
* 代表复合享元对象的外蕴状态,一个复合享元对象的所有单纯享元对象元素的外蕴状态 都是与复合
* 享元对象含有的单纯享元对象的内蕴状态一般是不相等的,不然就没有使用价值了。
*/
public class ConcreteCompositeFlyweight extends 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);
}
}
}
三、 享元模式适用的情况
1、一个系统有大量的对象。
2、这些对象耗费大量的内存。
3、这些对象的状态中的大部分都可以外部化。
4、这些对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。
5、软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。
享元模式的优点在于它大幅度地降低内在中对象的数量。但是,它做到这一点所付出的代价也是很高的:一是享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。二是享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
四、享元模式举例
一个文本编辑器往往会提供很多种字体,而通常的做法就是将每一个字母做成一个享元对象。享元对象的内蕴状态就是这个字母,而字母在文本中的位置和字模风格等其他信息则是外蕴状态。比如,字母a可能出现在文本的很多地方,虽然这些字母a的位置和字模风格不同,但是所有这些地方使用的都是同一个字母对象。这样一来,字母对象就可以在整个系统中共享。