享元模式是指一个对象含有某一种状态,而这种状态一旦被实例化之后就不能再改变了。所以通常这种含有状态的对象内部一定需要有一个用来存储状态值的变量,当对象实例化时就需要传入这个状态,从而将该状态进行确认,并且从此不能再改变。即通常的做法是通过构造方法时传入初始化状态,然后后面只提供一个get方法以给外部获取这个状态的值就可以了,这个在对象内部存储的状态就叫内蕴状态。内蕴状态只能是在实例化时确定,然后就不再改变。这个拥有不变状态的对象就是享元对象,不同的享元对象就是以这个内蕴状态进行区分的,理论上不应该出现具体相同内蕴状态的对象。
再者,涉及到享元对象时,还有一个外蕴状态的概念,这个概念是指享元对象通过一个方法,可以让外部对象传入一个状态值供这个享元对象使用,但是这个状态值是不会改变内蕴状态的值的,我们可以认为其就是个一般的参量传入,这种传入的变量所表示的状态就叫外蕴状态。为什么享元对象还需要外蕴状态呢,这是因为在实际应用中,我们发现一个用来表示状态的对象可以分为状态可变部分和状态不可变部分,我们将状态不可变的部分指定到一个特定的对象上,这样的对象就是一个享元对象,然后当我们需要这个享元对象再加上其他状态以构成含有复杂状态时就通过这个享元对象提供的接收外部参量的形式,从而通过这种传入外部状态加享元对象自己的内部状态以共同形成一个整体的状态,这就是享元模式存在的原因。
另外还必须指出的是,客户端不可以直接将具体享元类实例化,而必须通过一个工厂对象,利用一个factory()方法得到享元对象。一般而论,享元工厂对象在整个系统中只有一个,因此可以使用单例模式。通常这个工厂对象区别不同的享元对象时,其就是根据享元对象的内蕴状态进行区别的,由此也可以说明,每一个享元都是由工厂进行管理的,而工厂在区别不同的享元对象,就是根据每个享元对象的内蕴状态进行区分的,所以享元工厂内部通常有一个map类型的结构用来存储各个享元对象。
享元模式可以分为单纯的享元模式和和复合的享元模式。
单纯享元模式就是享元对象彼此独立地向享元工厂提供功能。下图为单纯享元模式的UML类图结构:
下面给出参考代码的实现:
抽象享元类Flyweight的代码如下:
abstract public class Flyweight
{
//参数state是一个示意性的外蕴状态传入
abstract public void operation(String state);
}
具体享元角色ConcreteFlyweight类的代码如下:
public class ConcreteFlyweight extends Flyweight
{
//存储的内蕴状态值
private Character intrinsicState = null;
//实例化时传入内蕴状态值,只初始化一次
publicConcreteFlyweight(Character state)
{
this.intrinsicState = state;
}
//业务操作,传入的状态为外蕴状态值
public void operation(Stringstate)
{
System.out.print( "\nIntrinsic State = " + intrinsicState +
", Extrinsic State = " + state);
}
}
本处为了简略起见,并未提供一个获取内蕴状态值的方法,实际情况下可以提供这样的一个方法,但一定要注意只可能提供一个get方法,而不能提供一个set方法,不然就违背了享元模式的基本精神。
享元工厂角色FlyweightFactory类的代码如下:
public class FlyweightFactory
{
private HashMap flies = newHashMap();
private Flyweight lnkFlyweight;
publicFlyweightFactory(){}
public synchronized Flyweightfactory(Character state)
{
if ( flies.containsKey( state) )
{
return (Flyweight) flies.get( state );
}
else
{
Flyweightfly = new ConcreteFlyweight( state );
flies.put( state, fly);
return fly;
}
}
public void checkFlyweight()
{
Flyweight fly;
int i = 0;
System.out.println("\n==========checkFlyweight()=============");
for ( Iteratorit = flies.entrySet().iterator() ; it.hasNext() ; )
{
Map.Entrye = (Map.Entry) it.next();
System.out.println("Item " + (++i) + " : " +e.getKey());
}
System.out.println("==========checkFlyweight()=============");
}
}
本处为简略起见,并未将该享元工厂实现为一个单实例类,实际情况下,可将其实现为单实例类。
客户端角色Client的代码如下:
public class Client
{
private static FlyweightFactory factory;
{ //创建一个享元工厂对象,如果是单实例,则直接用
factory = newFlyweightFactory();
//向享元工厂对象请求一个内蕴状态为'a'的享元对象
Flyweight fly= factory.factory(new Character('a'));
//以参量方式传入一个外蕴状态
fly.operation("First Call");
//向享元工厂对象请求一个内蕴状态为'b'的享元对象
fly = factory.factory(new Character('b'));
//以参量方式传入一个外蕴状态
fly.operation("Second Call");
//向享元工厂对象请求一个内蕴状态为'a'的享元对象
fly = factory.factory(new Character('a'));
//以参量方式传入一个外蕴状态
fly.operation("Third Call");
// intrinsic Flyweight
factory.checkFlyweight();
}
}
在单纯享元模式中,所有的享元对象都是单纯享元对象,也就是说都是可以直接通过享元工厂共享出去,而复合享元模式则是将一些单纯享元对象使用合成模式加以复合,形成复合享元对象,然后在享元工厂中一提供一些可以单纯访问享元对象的方法以及提供一个可以访问复合享元对象的方法。在这种情况下,复合享元对象是以整体的形式进行提供的,此时是不能够直接得到复合享元对象中的某一个具体享元对象的,只有通过分解,才能够获取复合享元对象中的单纯享元对象,这样就可以实现将复合享元对象中的单纯享元对象进行共享。
下图为复合享元模式典型的UML类图结构。
抽象享元角色Flyweight的代码如下:
抽象享元类Flyweight的代码如下:
abstract public class Flyweight
{
//参数state是一个示意性的外蕴状态传入
abstract public void operation(Stringstate);
}
具体享元角色ConcreteFlyweight类的代码如下:
public class ConcreteFlyweight extends Flyweight
{
//存储的内蕴状态值
private Character intrinsicState = null;
//实例化时传入内蕴状态值,只初始化一次
publicConcreteFlyweight(Character state)
{
this.intrinsicState = state;
}
//业务操作,传入的状态为外蕴状态值
public void operation(Stringstate)
{
System.out.print( "\nIntrinsic State = " + intrinsicState +
", Extrinsic State = " + state);
}
}
具体享元对象的代码如下:
public classConcreteCompositeFlyweight extends Flyweight
{
private HashMap flies = newHashMap(10);
//指向其父节点
private Flyweight flyweight;
publicConcreteCompositeFlyweight(){ }
public void add(Character key,Flyweight fly)
{
flies.put(key,fly);
}
public void operation(StringextrinsicState)
{
Flyweightfly = null;
for ( Iteratorit = flies.entrySet().iterator() ; it.hasNext() ; )
{
Map.Entrye = (Map.Entry) it.next();
fly =(Flyweight) e.getValue();
fly.operation(extrinsicState);
}
}
}
享元工厂的代码如下:
public class FlyweightFactory
{
//存储的是单纯享元对象
private HashMap flies = newHashMap();
private Flyweight lnkFlyweight;
publicFlyweightFactory(){}
//获取复合享元对象中的,参数为所需要复合享元对象的所有复合元素(也
//就是单纯享元对象)的内蕴状态。
public Flyweightfactory(String compositeState)
{
ConcreteCompositeFlyweightcompositeFly = new ConcreteCompositeFlyweight();
int length =compositeState.length();
Characterstate = null;
for(int i = 0; i <length; i++)
{
state = newCharacter(compositeState.charAt(i) );
System.out.println("factory(" + state + ")");
compositeFly.add(state, this.factory( state) );
}
return compositeFly;
}
//获取独立的单纯享元对象的工厂方法
public Flyweightfactory(Character state)
{
if ( flies.containsKey( state) )
{
return (Flyweight) flies.get( state );
}
else
{
Flyweightfly = new ConcreteFlyweight( state );
flies.put( state, fly);
return fly;
}
}
public void checkFlyweight()
{
Flyweight fly;
int i = 0 ;
System.out.println("\n==========checkFlyweight()=============");
for ( Iteratorit = flies.entrySet().iterator() ; it.hasNext() ; )
{
Map.Entrye = (Map.Entry) it.next();
System.out.println( "Item " + (++i) + " : " + e.getKey());
}
System.out.println("\n==========checkFlyweight()=============");
}
}
上面实现的获取复合享元对象的方法只是一个参考,实际上要比这个复杂得多,甚至需要对每一个复合享元对象进行存储的。
具体复合享元角色对象有以下两个责任:
1 复合享元对象是由单纯享元对象通过复合而成的,因此它提供了add()这样的聚集管理方法。由于一个复合享元对象具有不同的聚集元素,这些聚集元素在复合享元对象被创建之后加入,这就意味着复合享元对象的状态是会改变的,因此复合享元对象是不能共享的。这里所说的复合享元对象的状态会改变是指由于复合享元对象先于加入各单纯享元对象而存在,那么当一个复合享元对象被创建好之后,对于外部要加入多少单纯享元对象是不可知的,那么此时就说明当一个复合享元对象被创建之后,就不能具体某种固定的状态,可以用反证法证之。假设复合享元对象创建好之后,就拥有了一个内蕴状态。而这个状态应该指明这个复合状态所拥有的内部状态是不变的,这些内部状态是指的是内部的单纯享元对象等,而这时的内部单纯享元对象或者内部的复合享元对象还没有被传入,并且外部会加入多少单纯享元对象甚至再次加入复合享元对象不得而知,所以状态就不会稳定,这也就违背了享元模式中内蕴状态不变的原则,就不是享元模式了。所以具体复合享元对象是没有内蕴状态的。
2 复合享元角色实现了抽象享元角色所规定的接口,这里是operation()方法。这个方法有一个参量,代表复合享元对象的外蕴状态。一个复合享元对象的所有单纯享元对象元素的外蕴状态都是与复合享元对象的外蕴状态相等的;当然需要注意的是一个复合享元对象所含有的内部的单纯享元对象的内蕴状态是不应该相等的,不然就不叫单纯享元对象了。