1、面向对象的代价
1)面向对象很好地解决了系统抽象性的问题,同时在大多数情况下,也不会损及系统的性能。但是,在某些特殊的应用中,由于对象的数量太大,采用面向对象会给系统带来难以承受的内存开销。比如图形应用中的图元等对象、字处理应用中的字符对象等。
2、动机(Motivation)
1)采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。
2)如何在避免大量细粒对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?
3、意图(Intent)
运用共享技术有效地支持大量细粒度的对象。
——《设计模式》GoF
4、实例:每一个字符都应用一种字体
1)常规做法
//自己实现在一个Font
//Font对象占用内存(4+4+4(三个字段可见))+ (4+4(虚表指针、同步垃圾回收指针不可见))= 20bytes * n
public class Font
{
private string fontName;//一个指针占 4bytes
private int size; //整形int占 4bytes
private Color color; //对象也是指针占4bytes
}
//考虑这个对象的大小,它是细粒度的对象
//Charactor对象战用内存(2+4+20+2(有一个内存对齐填充的效应))+(4+4) = 36bytes
public class Charactor
{
private char c;//字符占16 bit, 2bytes //有一个内存对齐填充的效应
private Font f;//字符类型占20bytes 指针占4bytes
}
//客户
class System
{
public static void Main
{
//36bytes*100000=3600000 bytes =(3600k)-->3M数据
//如果是10000000个对象就占用300M数据了
ArrayList list = new ArrayList(100000);
for (int i = 0; i < list.Count; i++)
{
//这个字符对象不一定要new,有可能通过其他方式得到,比如序列化
Charactor c = new Charactor();
list.Add(c);
}
}
}
2)就用Flyweight模式共享Font对象的内存空间
public class Font
{
private string fontName;
private int size;
private Color color;
//重写Font.Equals()方法
public override bool Equals(Font font)
{
}
}
public class Charactor
{
private char c;
private Font f;
//保存已存在的字体对象
//这里可以抽象出一个简单的工厂模式,动态得到存在的font
private static Hashtable fontTable = new Hashtable();
public char Chr
{
get
{
return c;
}
set
{
c = value;
}
}
public Font CFont
{
get
{
return f;
}
set
{
//在设置字体时,如果已经有了这个字体对象,我们就不需要new了
//只需要将指针指向已经存在的这个字体对象
if (fontTable.Keys.Exist(f))
{
this.f = fontTable.Keys[f];
}
else
{
fontTable.Keys.Add(f);
this.f = value;
}
}
}
}
//客户
class System
{
public static void Main
{
ArrayList list = new ArrayList(100000);
Font f1 = new Font("宋体", 5, Color.Red);
Font f2 = new Font("宋体", 5, Color.Red);
Charactor c1 = new Charactor();
c1.Chr = 'a';
c1.CFont = f1;
Charactor c2 = new Charactor();
c2.Chr = 'a';
c2.CFont = f2;
//c2应用的字体对象指向的同一块内存空间
}
}
5、Flyweight模式的几个要点
1)面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
2)Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
3)对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。