1. 模式意图
运用共享技术有效地支持大量细粒度的对象;
如想让某个类的一个实例能用来提供许多“虚拟实例”,就是用享元模式;
Flyweight模式对那些通常因为数量太大而难以用对象来表示的概念或实体进行建模。
享元对象能做到共享的关键是区分内部状态和外部状态:
内部状态:存储在享元对象内部并且不会随环境改变而改变,因此内部状态可以共享;
外部状态:随环境改变而改变的、不可以共享的状态;享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入享元对象内部;
享元的优点: 减少运行时对象实例的个数,节省内存;将许多“虚拟”对象的状态集中管理;
享元的用途和缺点:当一个类有许多的实例,而这些实例能被同一个方法控制的时候,我们就可以使用享元模式;享元模式的缺点在于,一旦实现了它,那么单个逻辑实例将无法拥有独立而不同的行为;
2. 模式定义
2.1 单纯享元模式
在单纯的享元模式中,所有的享元对象都是可以共享的。单纯享元模式所涉及的角色如下:
抽象享元(Flyweight): 是所有的具体享元类的超类,为这些类规定出所要实现的公共接口。那些需要外部状态的操作可以通过调用方法以参数形式传入;
具体享元(ConcreteFlyweight):实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享;
享元工厂(FlyweightFactory): 负责创建和管理享元,保证享元对象可以被系统适当的共享;当一个客户端对象调用一个享元对象的时候,享元工厂会检查系统中是否已经有一个符合要求的享元对象;如果已经有了,享元工厂应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂就应当创建一个合适的享元对象;
Client: 维护一个对所有享元对象的引用,需要自行存储所有享元对象的外部状态;
2.2 复合享元模式
单纯享元模式中,所有的享元对象都可以直接共享。复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享;
复合享元(UnsharableFlyweight):复合享元所代表的对象不可以共享,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合;
3. 模式实现
4. 模式应用
4.1 Flyweight与Composite模式
Flyweight模式经常和Composite模式结合起来表示一个层次结构,这一层次式结构是一个共享叶节点的图。共享的结果是,Flyweight的叶节点不能存储指向父节点的指针。而父节点的指针将传给Flyweight作为它的外部状态的一部分。
例如,3D CAD系统中的装配体表示,是一个非循环有向图(A Directed Acyclic Graph DAG),最下一层叶节点为图形节点;一个图形节点可以被多个零件模型所共享;如下的装配体Quad的结构如下:
5. 小结
当一下所有的条件都满足时,可以考虑使用享元模式:
- 一个系统有大量的对象;
- 这些对象耗费大量的内存;
- 这些对象的状态中的大部分都可以外部化;
- 这些对象可以按照内部状态分成很多的组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替;
- 软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的;
使用享元模式需要维护一个记录了系统已有的所有的享元的表,而这需要耗费资源。因粗,应当在有足够多的享元实例可供共享时才值得使用享元模式。