享元模式/Flyweight
意图/适用场景:
享元模式也叫轻量模式(flyweight pattern),因应用大量轻量级对象而得名。
Flyweight模式对那些通常因为数量太大而难以用对象来表示的概念或实体进行建模。例如,文档编辑器可以为字母表中的每一个字母创建一个flyweight。每个flyweight存储一个字符代码,但它在文档中的位置和排版风格可以在字符出现时由正文排版算法和使用的格式化命令决定。字符代码是内部状态,而其他的信息则是外部状态。
享元对象能做到共享的关键是区分内部状态和外部状态。内部状态存储在享元对象内部,并且是不会随环境改变而有所不同的,这是享元的本质特征。个部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入享元对象中去。外部状态不可以影响享元对象的内部状态,它们是互相独立的。
UML:
参与者:
- 抽象享元角色:此角色是所有具体享元类的公共接口。
- 具体享元角色:实现抽象享元角色所元宝的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,这是共享的前提。
- 享元工厂角色:此角色负责创建和管理享元角色,必须保证享元对象可以被系统适当地共享 。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否有一个符合要求的享元对象。如果有,享元工厂就应当直接提供已有的享元对象;如果没有,享元工厂就应当创建一个合适的享元对象,并提供给客户端。
- 客户端角色:本角色需要维护一个对所有享元对象的引用,并自行存储所有享元对象的外部状态。
要点:
Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。当以下情况都成立时使用Flyweight模式:
- 一个应用程序使用了大量的对象。
- 完全由于使用大量的对象,造成很大的存储开销。
- 对象的大多数状态都可变为外部状态。
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
扩展:
复合享元模式:
下面考虑一个较为复杂的情况,即将一些单纯享元使用合成模式加以复合,形成复合享元对象。这样的复合享元对象本身不能共享。
复合享元模式比单纯享元模式多一种角色:
- 复合享元角色:此角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象,它实际上是享元模式的一种应用。
复合享元角色是由单纯享元对象通过复合而成,因此它提供了add()这样的聚合集管理方法。由于一个复合享元对象具有不同的聚合元素,这些聚合元素在复合享元对象被创建之后加入,这本身就意味着复合享元对象的状态是会改变的,因此复合享元对象是不能共享的。
复合享元角色实现了抽象享元角色所规定的接口,也就是operation()方法。这个方法有一个参量,代表复合享元对象的外部状态。一个复合享元对象的所有单纯享元对象元素的外部 状态都是与复合享元对象的外部状态相等的;而一个复合享元对象所含有的单纯享元对象的内部状态一般是不相等的,不然就没有使用价值了。
相关模式:
在应用享元模式的场景中,一般会有大量的享元对象。为了更有效率地实现共享,享元对象最好是禁止由客户端自行创建,只能由享元工厂来创建和管理。
可以在享元工厂应用以下两种模式可以起到优化的作用:
- 工厂模式:以此来管理享元对象。
- 单例模式:以此来保证享元对象和享元工厂都只有一个实例。
示例代码:
[java]
// Source code from file:CompositeConcreteFlyweig ht.java
packagedesignPatterns.Flyweight;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
publicclass CompositeConcreteFlyweig ht implements Flyweight {
privateHashMap maps = new HashMap();
publicvoid add(Character key, Flyweight fly) {
maps.put(key, fly);
}
publicvoid operation(String state) {
Flyweight fly =null;
for(Iterator it = maps.entrySet().iterator(); it.hasNext();) {
Map.Entrye = (Map.Entry)it.next();
fly =(Flyweight)e.getValue();
fly.operation(state);
}
}
}
// Source code from file:ConcreteFlyweight.java
packagedesignPatterns.Flyweight;
publicclass ConcreteFlyweight implements Flyweight {
privateCharacter internalState = null;
publicConcreteFlyweight(Character state) {
this.internalState= state;
}
publicvoid operation(String state) {
System.out.println("internal state = "internalState ", external state = " state);
}
}
// Source code from file:Flyweight.java
packagedesignPatterns.Flyweight;
publicinterface Flyweight {
publicvoid operation(String state);
}
// Source code from file:FlyweightFactory.java
packagedesignPatterns.Flyweight;
import java.util.HashMap;
publicclass FlyweightFactory {
privateHashMap maps = new HashMap();
publicFlyweight factory(Character state) {
if(maps.containsKey(state)) {
return(Flyweight)maps.get(state);
}else {
Flyweight flyweight =new ConcreteFlyweight(state);
maps.put(state, flyweight);
returnflyweight;
}
}
publicFlyweight factory(String compositeState) {
CompositeConcreteFlyweight compositeFly = newCompositeConcreteFlyw eight();
intlength = compositeState.length();
Characterstate = null;
for(inti = 0; i < length; i ) {
state =new Character(compositeState.charAt(i));
compositeFly.add(state,this.factory(state));
}
returncompositeFly;
}
}
[/java]