场景:程序需要不断创建大量相似的细粒度对象,会造成严重的内存负载。我们可以选择享元模式解决该问题。
享元抽象:Flyweight 描述享元的抽象结构。它包含内蕴和外蕴部分(别被术语迷惑,这是一种比较深的描述,浅的来说,内蕴就是对象中不变的数据,外蕴反之,在编程时区分两者的区别就可以了,无需刻意在代码中表达这种术语的含义。)
享元:ConcreteFlyweight 可用用来共享的对象实例,我们将享元的按照共享性质分为内蕴(不因环境变化而变的部分),外蕴两部分。内蕴是享元的固有特性,不会变化。外蕴需要享元被调用时由外部设置。
享元池(或叫享元工厂):FlyweightFactory存放享元的地方。
非共享享元:UnsharedFlyweight 与享元都继承自享元抽象,但其不能被共享。其应用在需要将一个享元组合一起使用,并统一设置外蕴的情景。比如为了提高执行速度,将经常重复出现的对象组(其中每个对象也可以是从享元池中共享抽取出来的)封装到一起,从而执行重复的工作。比如说制图中,由几个基本形状组成的复合形状需要经常被提取出来执行绘制操作。
模型图如下:
示例:
//在一个任务中客户需要绘制大量的正方形
//其中这些正方形在填充颜色和尺寸上仅有有限的几个固定值。
//边长是5,10,15,颜色,红,绿,蓝,这代表客户端需求按边长,颜色分组最多有3*3=9组类似的正方形对象。
//如果每次需要一个正方形对象,其中每个对象关于填充颜色的数据很大,如果都要一一实例化,将会是内存读写,负载不断增大。
//如果采用享元模式,在享元池中最多存放9种不同内蕴的实例。那么将大大减少内存负载。
//经常会出现一些同样的组合形状被重复绘制
//为了提高运行效率,可以将这些重复出现的对象封装到一起执行任务
//定义享元抽象Flyweight public abstract class Rectangle { //对象的坐标(属于对象的外蕴部分) public float PointX { get; set; } public float PointY { get; set; } //内蕴 public string Color { get; set; } //边长 public float Width { get; set; } //设置外蕴 public abstract void SetStart(float x,float y); } //定义具体享元 public class RedRectangle:Rectangle { public RedRectangle() { this.Color = "Red"; } public override void SetStart(float x,float y) { this.PointX = x; this.PointY = y; } } public class GreenRectangle : Rectangle { public GreenRectangle() { this.Color = "Green"; } public override void SetStart(float x,float y) { this.PointX = x;this.PointY = y; } } public class BlueRectangle : Rectangle { public BlueRectangle() { this.Color = "Blue"; } public override void SetStart(float x,float y) { this.PointX = x; this.PointY = y; } } //定义一个非共享元 public class UshareRectangle : Rectangle { public List<Rectangle> Rectangles { get; set; } public UshareRectangle() { } public override void SetStart(float x, float y) { for(int i=0;i<Rectangles.Count;i++) { Rectangles[0].PointX = x+i*15; Rectangles[0].PointX = y + i * 15; } } } //定义享元池 public class FlyweightPool { public Hashtable Pool { get; set; } } //定义一个客户端 public static class Client { public static void DrawRectangle(Rectangle rectangle,float x,float y) { rectangle.SetStart(x, y); Console.WriteLine("您在({0},{1})处画了一个边长为{2}的{3}正方形" ,rectangle.PointX,rectangle.PointY,rectangle.Width,rectangle.Color); } } //测试代码 public class Test { private FlyweightPool pool = new FlyweightPool() { Pool = new Hashtable() };//定义一个工厂实例
//在平台上画上2000个随机指定类型的正方形 public void Test1() { //画图开始 //在指定的2000个位置画总共2000个正方形 float[][] position = new float[2000][]; for (int i = 0; i < 2000; i++) { position[i] = new float[2]; position[i][0] = i * 15; position[i][1] = i * 15; } Random random = new Random(1); for (int i = 0; i < 2000; i++) { switch (random.Next(8)) { case 0: if (pool.Pool["r5"]==null) { pool.Pool.Add("r5", new RedRectangle() { Color = "Red", Width = 5 }); } Client.DrawRectangle((Rectangle)pool.Pool["r5"],position[i][0],position[i][1]); break; case 1: if (pool.Pool["r10"] == null) { pool.Pool.Add("r10", new RedRectangle() { Color = "Red", Width = 10 }); } Client.DrawRectangle((Rectangle)pool.Pool["r10"], position[i][0], position[i][1]); break; case 2: if (pool.Pool["r15"] == null) { pool.Pool.Add("r15", new RedRectangle() { Color = "Red", Width = 15 }); } Client.DrawRectangle((Rectangle)pool.Pool["r15"], position[i][0], position[i][1]); break; case 3: if (pool.Pool["g5"] == null) { pool.Pool.Add("g5", new GreenRectangle() { Color = "Green", Width = 5 }); } Client.DrawRectangle((Rectangle)pool.Pool["g5"], position[i][0], position[i][1]); break; case 4: if (pool.Pool["g10"] == null) { pool.Pool.Add("g10", new GreenRectangle() { Color = "Green", Width = 10 }); } Client.DrawRectangle((Rectangle)pool.Pool["g10"], position[i][0], position[i][1]); break; case 5: if (pool.Pool["g15"] == null) { pool.Pool.Add("g15", new GreenRectangle() { Color = "Green", Width = 15 }); } Client.DrawRectangle((Rectangle)pool.Pool["g15"], position[i][0], position[i][1]); break; case 6: if (pool.Pool["b5"] == null) { pool.Pool.Add("b5", new BlueRectangle() { Color = "Blue", Width = 5 }); } Client.DrawRectangle((Rectangle)pool.Pool["b5"], position[i][0], position[i][1]); break; case 7: if (pool.Pool["b10"] == null) { pool.Pool.Add("b10", new BlueRectangle() { Color = "Blue", Width = 10 }); } Client.DrawRectangle((Rectangle)pool.Pool["b10"], position[i][0], position[i][1]); break; case 8: if (pool.Pool["b15"] == null) { pool.Pool.Add("b15", new BlueRectangle() { Color = "Blue", Width = 15 }); } Client.DrawRectangle((Rectangle)pool.Pool["b15"], position[i][0], position[i][1]); break; default: break; } } } //重复绘制图形组合 public void Test2() { //重复绘制某组合 UshareRectangle urs = new UshareRectangle(); if (pool.Pool["r5"] == null) { pool.Pool.Add("r5", new RedRectangle() { Color = "Red", Width = 5 }); } urs.Rectangles.Add((Rectangle)pool.Pool["r5"]); if (pool.Pool["r10"] == null) { pool.Pool.Add("r10", new RedRectangle() { Color = "Red", Width = 5 }); } urs.Rectangles.Add((Rectangle)pool.Pool["r10"]); int x = 0, y = 0; for (int i = 0; i < 20; i++) { foreach (Rectangle r in urs.Rectangles) { Client.DrawRectangle(r, x,y); x += 15;y += 15; } } } }