FLYWEIGHT享元模式
1、 意图
运用共享技术有效地支持大量细粒度的对象。
2、 适用性
在下面情况下可以使用FLYWEIGHT模式
- 一个应用程序使用了大量的对象。
- 完全由于使用大量的对象,造成很大的存储开销。
- 对象的大多数状态都可变为外部状态。
- 如果删除了对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖与对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
3、 结构
4、 参与者
Flyweight
——描述一个接口,通过这个接口flyweight可以接受并作用于外部状态。
ConcreteFlyweight
——实现Flyweight接口,并为内部状态(如果有的话)增加存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的;即,它必须独立于ConcreteFlyweight对象的场景。
UnsharedConcreteFlyweight
——并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。在Flyweight对象结构的某些层次,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点。
FlyweightFactory
——创建并管理flyweight对象。
——确保合理地共享flyweight。当用户请求一个flyweight时,FlyweightFactory对象提供一个已经创建的实例或者创建一个。
Client
——维持一个对Flyweight的引用。
——计算或存储一个(多个)flyweight的外部状态。
5、 协作
1) Flyweight执行时所需的状态必定是内部或外部的。内部状态存储于ConcreteFlyweight对象之中;而外部对象则由Client对象存储或计算。当用户调用Flyweight对象的操作时,将该状态传递给它。
2) 用户不应直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证他们适当地进行共享。
6、 效果
使用Flyweight模式时,传输、查找和/或计算外部状态都会产生运行时的开销,尤其当flyweight原先被存储为内部状态时。然而,空间上的节省抵消了这些开销。共享的Flyweight越多,空间节省越大。节约空间由以下几个元素决定:
- 因为共享,实例总数减少的数目
- 对象内部状态的平均数目
- 外部状态是计算的还是存储的
共享的Flyweight越多,存储节约也就越多。节约量随着共享状态的增多而增大。当对象使用大量的内部及外部状态,并且外部状态是计算出来的而非存储的时候,节约量将达到最大。所以,可以使用两种方法来节约存储:用共享减少内部状态的消耗,用计算时间换取对外步状态的存储。
7、 实现
在实现Flyweight模式时应该注意以下:
1)删除外部状态;该状态的可用性在很大程度上取决于是否容易识别外部状态并将它从共享对象中删除。如果不同种类的外部状态和共享前对象的数目是相同的话,删除外部状态不会降低存储消耗。理想的状况是,外部状态可以由一个单独的对象结构计算得到,且该结构的存储要求非常小。
2)管理共享对象;因为对象是共享的,用户不能直接对它进行实例化,因此FlyweightFactory可以帮助用户查找某个特定的Flyweight对象。FlyweightFactory对象经常使用关联存储帮助用户查找感兴趣的Flyweight。
8、 代码示例
Flyweight:
package com.examples.pattern.flyweight;
/**
* 享元接口,通过这个接口享元可以接受并作用于外部状态
*/
public interface Flyweight {
public void operation(String extrinsicState);
}
ConcreteFlyweight:
package com.examples.pattern.flyweight;
/**
* 享元对象
*/
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
/**
* 构造方法,传入享元对象的内部状态的数据
*
* @param intrinsicState
*/
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
// 具体的功能处理,可能会用到享元内部、外部状态
System.out.println("ConcreteFlyweight: " + extrinsicState);
}
}
UnsharedConcreteFlyweight:
package com.examples.pattern.flyweight;
/**
* 不需要共享的Flyweight对象; 通常是将被共享的享元对象作为子节点组合出来的对象。
*/
public class UnsharedConcreteFlyweight implements Flyweight {
private String allState;
@Override
public void operation(String extrinsicState) {
System.out.println("UnsharedConcreteFlyweight: " + extrinsicState);
}
}
FlyweightFactory:
package com.examples.pattern.flyweight;
import java.util.HashMap;
import java.util.Map;
/**
* 享元工厂
*/
public class FlyweightFactory {
/**
* 缓存多个Flyweight对象
*/
private Map<String, Flyweight> fsMap = new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
// 1: 先从缓存中查找,是否存在key对应的Flyweight对象
Flyweight f = fsMap.get(key);
// 2: 如果存在,就返回相对的Flyweight对象
if (f == null) {
// 3: 如果不存在
// 3.1 创建一个新的Flyweight对象
f = new ConcreteFlyweight(key);
// 3.2把这个新的Flyweight对象添加到缓存中
fsMap.put(key, f);
}
// 3.3然后返回这个新的Flyweight对象
return f;
}
public int getSize() {
return fsMap.size();
}
}
Client:
package com.examples.pattern.flyweight;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
FlyweightFactory ff = new FlyweightFactory();
Flyweight fw_1 = ff.getFlyweight("fw_1");
fw_1.operation("fw_1");
Flyweight fw_2 = ff.getFlyweight("fw_2");
fw_2.operation("fw_2");
Flyweight fw_3 = ff.getFlyweight("fw_2");
fw_3.operation("fw_2");
System.out.println(ff.getSize());
}
}
9、 相关模式
Flyweight模式通常和Composite模式结合起来,用共享叶节点的有向无环图实现一个逻辑上的层次结构。