一个类A有诸多的成员变量,创建一个A对象需要在堆中为该对象分配空间。假设,需要创建十万个A对象,那么A的成员变量中,有没有一成不变的某些数据的组合呢?如果能够将这些数据组合加以类型化——称为轻量级或鸿毛/羽毛/Flyweight对象,那么十万个A对象可以共享Flyweight对象,从而有效地降低内存开销。
Flyweight设计模式,直译为轻量级、鸿毛模式,意译为共享单元模式简称享元模式。
属性对象
假设Car对象有如下属性:
String owner;//车主,例程中略
String name;//车型名称
Color color;//车的颜色
int X,Y; //GPS定位坐标
int length,width,height;//车体的几何尺寸
如果创建十几个Car对象,我们不需要过多考虑。如果创建武汉市所有轿车的对象,那么Car对象的成员变量就需要仔细分析。①GPS定位坐标X和Y,是每时每刻都在变化的,不考虑共享;车主owner,大多数Car的owner是私人,除非owner是公交或的士公司才考虑它;车的颜色变化也比较多;②车型名称和车体的几何尺寸,这种数据组合是固定的,虽然奥迪A6和奥迪A4分别有不同取值,但数据组合是固定的。于是,可以设计一个鸿毛flyweight类型,用于保存数据组合。package property.flyweight;
public interface ICarUnit{
public String getName();
public double getHeight();
public double getWidth();
public double getLength();
public void printMess(String mess);
}//具体子类暂缓
ICarUnit对象将被Car对象共享。例如,创建的表示奥迪A6、奥迪A4、东风标致2008、东风标致4008的羽毛对象都将被共享。
如何管理共享的对象?
通常
- 每种享元对象仅创建一个,而且可以通过一个HashMap管理这些对象。
- 外界不得创建享元对象,而是使用HashMap所管理的共享对象。
因而,管理共享对象的工作,抽象并封装成一个类。GoF中,称其为享元工厂(FlyweightFactory),而yqj2065通常称其为享元池(FlyweightPool)。
享元工厂这一称呼,从内部说明了该类包含一个静态工厂,并按照HashMap的key获得羽毛对象,如方法
public static synchronized ICarUnit getFlyweight(String key)
享元池这一称呼,从外界用户的感受,说明该类的作用就是维护一个对象池。真正持有数据的享元CarUnit(即ICarUnit的实现类),因为和享元池/享元工厂关系密切,通常设计为享元池的内部类。package property.flyweight;
import java.util.HashMap;
public class FlyweightPool{
private static HashMap<String,ICarUnit> hashMap;
static{hashMap=new HashMap<String,ICarUnit>();}
private FlyweightPool(){ }
public static synchronized ICarUnit getUnit(String name,double length,double width,double height){
String key = name;
if(hashMap.containsKey(key))
return hashMap.get(key);
else{
ICarUnit flyweight = new CarUnit(name,width,height,length);
hashMap.put(key,flyweight);
return flyweight;
}
}
static final class CarUnit implements ICarUnit{
private String name;
private double width;
private double height;
private double length;
private CarUnit(String name,double width,double height,double length){
this.name =name;
this.width=width;
this.height=height;
this.length=length;
}
public String getName(){ return name; }
public double getHeight(){ return height; }
public double getWidth(){ return width; }
public double getLength(){ return length; }
public void printMess(String mess){
System.out.print(mess); //输出外部数据mess
System.out.print(" 宽度:"+width); //输出内部数据width
System.out.print(" 高度:"+height);
System.out.println("长度:"+length);
}
}
}
JVM中的享元
Integer 、 Byte、Short、Long和Character使用了享元模式。Character (from 0 to 127) 、其他(from -128 to 127)。而最典型的则是String。