你的位置:首页 > ASP.net教程

[ASP.net教程]设计模式C#实现(十三)——享元模式(蝇量模式)


 

      • 意图
      • 适用性
      • 结构
      • 实现
      • 效果
      • 参考

 

意图

运用共享技术有效地支持大量细粒度的对象。

适用性

当以下情况都成立时使用:

  • 一个程序使用了大量的对象
  • 完全由于使用大量对象造成很大存储开销
  • 对象的大多数状态都可以变为外部状态
  • 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象
  • 应用程序不依赖对象标识

结构

蝇量UML
以下摘自参考1

享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最常见、最直观的就是内存的损耗。享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。
  一个内蕴状态是存储在享元对象内部的,并且是不会随环境的改变而有所不同。因此,一个享元可以具有内蕴状态并可以共享。
  一个外蕴状态是随环境的改变而改变的、不可以共享的。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态不可以影响享元对象的内蕴状态,它们是相互独立的。

实现

现在用程序模拟各种果树,假设树龄是果树的外部状态,而果树的种类是果树的内部状态。
先来实现抽象果树,它有一个方法,这个方法依靠外部状态——树龄

 public abstract class Tree  {    protected string Type;    public virtual void PrintDescription(int age)    {      Console.WriteLine(Type + ": " + age);    }  }

具体的果树,内部状态需要在实例化时传入

 public class ConcreteTree:Tree  {    public ConcreteTree(string type)    {      Type = type;    }  }

果树工厂负责创建和管理享元角色

  public class TreeFactory  {    private Dictionary<string, Tree> _trees = new Dictionary<string, Tree>();    public Tree GetTree(string type)//获取对象    {      Tree tree;      var result = _trees.TryGetValue(type, out tree);//查找该类型对象是否存在      if (!result)      {        tree = new ConcreteTree(type);//不存在则实例化对象        _trees.Add(type, tree);      }      return tree;    }  }

客户类

 class Program  {    static void Main(string[] args)    {      var factory = new TreeFactory();      var tree1 = factory.GetTree("AppleTree");      tree1.PrintDescription(10);      var tree2 = factory.GetTree("PeerTree");      tree2.PrintDescription(12);      var tree3 = factory.GetTree("AppleTree");      tree3.PrintDescription(8);      Console.WriteLine(tree1==tree3);      Console.ReadKey();    }  }

运行结果
运行结果
从结果看,tree1和tree3是同一个对象实例,因此当我们有大量的同类果树时,只需要维护一个实例对象和一个树龄数组从而节省了存储空间。

效果

  • 节省存储空间
  • 逻辑比直接维护多个实例复杂

参考

  1. 《JAVA与模式》之享元模式
  2. 《Head First 设计模式》
  3. 《设计模式》