2.6 FLYWEIGHT
2.6.1 功能
看到Flyweight想起来线程叫做lightweight进程:
Lightweight:a fighter who is in a class of boxers weighing from 125 to 132 pounds (57 to 60kilograms)
Flyweight:a fighter in a class of boxers who dos not weigh more than 112 pounds (51kilograms)
看这个词知道这个对象是多么的轻量级,可是单个屌丝是弱小的,由屌丝组成的人民群众是很强大的。因此Flyweight要解决的问题是:假如这种对象多如牛毛,那么尝试将对象的状态分为内部状态和外部状态,如果内部数量和外部数量都很少,那么就皆大欢喜,这些外部状态有单独的对象去管理,而剩下的内部状态的对象以共享方式存在(即相同的内部状态只需要一个对象).这个过程就如同:本来是一个笛卡尔积,现在拆成两列存储,那么总数量将急剧减少。所以这种实现是以计算能力换内存空间的做法。
2.6.2结构
一旦一个类会出现不同的对象且对象间有一定的区别,就得瞄一瞄对象图了:
参与者:
• F l y w e i g h t
— 描述一个接口,通过这个接口f l y w e i g h t可以接受并作用于外部状态。
• C o n c r e t e F l y w e i g h t
— 实 现F l y w e i g h t 接 口 , 并 为 内 部 状 态 ( 如 果 有 的 话 ) 增 加 存 储 空 间 。Co n c r e t e F l y w e i g h t对象必须是可共享的。它所存储的状态必须是内部的;即,它必须独立于C o nc r e t e F l y w e i g h t对象的场景。
• U n s h a r e d C o n c r e t e F l y w e i gh t
— 并非所有的F l y w e i g h t子类都需要被共享。F l y w e i g h t接口使共享成为可能,但它并不强制共享。在F l y w e i g h t对象结构的某些层次,U n s h a r e d C o n c r e t e F l y w e i g h t对象通常将C o n c r e t e F l y w ei g h t对象作为子节点(R o w和Co l u m n就是这样) 。
• F l y w e i g h t F a c t o r y
— 创建并管理f l y w e i g h t对象。
— 确保合理地共享f l y w e i g h t。当用户请求一个f l y w e i g h t 时,Fl y w e i g h t F a c t o r y对象提供一个已创建的实例或者创建一个(如果不存在的话) 。
• Client
— 维持一个对f l y w e i g h t的引用。
— 计算或存储一个(多个) f l y w e i g h t的外部状态。
协作:
•f l y w e i g h t执行时所需的状态必定是内部的或外部的。内部状态存储于C o n c r e t e F l y w e i g h t对象之中;而外部对象则由 C l i e n t对象存储或计算。当用户调用 f l y w e i g h t对象的操作时,将该状态传递给它。
• 用户不应直接对C o n c r e t e F l y w e i g h t类进行实例化,而只能从 F l y w e i g h t F a c to r y对象得到Co n c r e t e F l y w e i g h t对象,这可以保证对它们适当地进行共享。
2.6.3 Java例子
下面的例子转自网上,表示了Flyweight模式的核心:
package DesignPattern;
/**
*************************************
* @Title Flyweight.java
* @Author 张作强
* @Date 2010-8-21
* @Comment抽象享元(Flyweight)角色
* 此角色是所有的具体享元类的超类,
* 为这些类规定出需要实现的公共接口。
* 那些需要外蕴状态(External State)的操作可以通过调用商业方法以参数形式传入。
*************************************
*/
public abstract class Flyweight {
public abstract void Operation(int extrinsicState);
}
package DesignPattern;
/**
*************************************
* @Title ConcreteFlyweight.java
* @Author 张作强
* @Date 2010-8-21
* @Comment具体享元(ConcreteFlyweight)角色
* 实现抽象享元角色所规定的接口。
* 如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
* 享元对象的内蕴状态必须与对象所处的周围环境无关,
* 从而使得享元对象可以在系统内共享的。
*************************************
*/
public class ConcreteFlyweight extends Flyweight {
private String intrinsicState = "A";
@Override
public void Operation(int extrinsicState) {
System.out.println("ConcreteFlyweight:intrinsicstate : " +intrinsicState + " , extrinsicstate : " + extrinsicState);
}
}
package DesignPattern;
import java.util.Hashtable;
/**
*************************************
* @Title FlyweightFactory.java
* @Author 张作强
* @Date 2010-8-21
* @Comment享元工厂(FlyweightFactory)角色
* 本角色负责创建和管理享元角色。
* 本角色必须保证享元对象可以被系统适当地共享。
* 当一个客户端对象调用一个享元对象的时候,
* 享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。
* 如果已经有了,享元工厂角色就应当提供这个已有的享元对象;
* 如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
*************************************
*/
public class FlyWeightFactory {
private Hashtable<String,Flyweight>flyweights = new Hashtable<String, Flyweight>();
public void addFlyweight(Strings , Flyweight f) {
flyweights.put(s, f);
}
public FlyweightgetFlyweight(String key){
Flyweightflyweight = (Flyweight)flyweights.get(key);
if(flyweight ==null){
flyweight= new ConcreteFlyweight();
flyweights.put(key, flyweight);
}
return flyweight;
}
public intgetFlyweightFactorySize(){
return flyweights.size();
}
}
package DesignPattern;
/**
*************************************
* @Title Client.java
* @Author 张作强
* @Date 2010-8-21
* @Comment客户端(Client)角色
* 本角色需要维护一个对所有享元对象的引用。
* 本角色需要自行存储所有享元对象的外蕴状态。
*************************************
*/
public class Client {
public static void main(String[] args){
int extrinsicState = 22;
FlyWeightFactoryf = new FlyWeightFactory();
f.addFlyweight("X", newConcreteFlyweight());
f.addFlyweight("Y", newConcreteFlyweight());
// f.addFlyweight("Z",new ConcreteFlyweight());
Flyweightfx = f.getFlyweight("X");
fx.Operation(extrinsicState);
Flyweightfy = f.getFlyweight("Y");
fy.Operation(extrinsicState);
// 当一个客户端对象调用一个享元对象的时候,
// 享元工厂角色会检查系统中是否已经有一个复合要求的享元对象。
// 如果已经有了,享元工厂角色就应当提供这个已有的享元对象;
// 如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
Flyweightfz = f.getFlyweight("Z");
fz.Operation(extrinsicState);
System.out.println("FlyweightFactorySize: "+ f.getFlyweightFactorySize());
}
}