设计模式11: Flyweight 享元模式(结构型模式)
Flyweight 享元模式(结构型模式)
面向对象的代价
面向对象很好的解决了系统抽象性的问题,同时在大多数情况下也不会损及系统的性能。但是,在某些特殊应用中,由于对象的数量太大,采用面向对象会给系统带来难以承受的内存开销。比如图形应用中的图元等对象、字处理应用中的字符对象等。
动机(Motivation)
采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,而带来很高的运行代价——主要指内存需求方面的代价。
如何避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象方式进行操作?
意图(Intent)
运用共享技术有效地支持大量细粒度的对象——《设计模式》GoF
class Font//(4+4+4)bytes+8bytes=20bytes (8bytes:4bytes垃圾回收控制位,4bytes虚表指针) { public string fontName;//4bytes 只是举例子,实际中应该用属性的 public int size;//4bytes public Color color;//4bytes
public Font(string fontName,int size,Color color)
{
this.fontName = fontName;
this.size = size;
this.color = color;
}
} /* 4:string fontName,字符串比较特殊,由于已经用了字符串池,就不考虑它在堆中的大小,这里只计算了它的指针大小。 4:int size 4:Color color 8:4bytes垃圾回收控制位+4bytes虚表指针 共20bytes */ class Charactor//(2+4+20+2)bytes+8bytes=36bytes { pubic char chr;//2bytes pubic Font font;//20bytes } /* 2:char chr 4:Font font,指针占4bytes 20:Font font 堆中大小 2:32位系统的4bytes填充效应,char chr只占2bytes,补充了2bytes 8:4bytes垃圾回收控制位+4bytes虚表指针 共36bytes */ class System { public static void Main() { List<Charactor> list=new List<Charactor>(); for (int i = 0; i < 100000; i++) { Charactor charactor=new Charactor(); list.Add(charactor); } //36bytes*100000=3600000bytes≈3600k≈3.6M //如果是1千万个对象结果就是大约360M,这就太大了,要考虑享元模式。 } }
当Charactor 对象个数非常多时,就考虑使用享元模式了。需要对Charactor 进行改造:
class Charactor { public char chr; private Font font; private static Dictionary<string,Font> dicFont; public Font CFunt { get { return font; } set { string key = value.fontName + value.size + value.color; if (dicFont.Keys.Contains(key)) { this.font = dicFont[key]; } else { dicFont.Add(key, value); this.font = value; } } } }
这样当出现重复的字体的时候就不会占用多余的空间了。
Charactor c1=new Charactor(); Font f1= new Font("宋体", 10, Color.Red); c1.CFunt = f1; Charactor c2 = new Charactor(); Font f2 = new Font("宋体", 10, Color.Red); c2.CFunt = f2;
f1和f2是不同对象,它们指向不同的内存地址,但是在给c1,和c2的CFont赋值时,由于用了Dictionary<string,Font>,对c2的CFont就没有分配新的内存空间。当对象非常多的时候,这样可以节省很多的空间。
.NET中字符串池就是运用了享元模式。
Flyweight模式的几个要点
- 面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
- Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
- 对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。