意图
运用共享技术有效地支持大量细粒度的对象。
适用性
当以下情况都成立时使用:
- 一个程序使用了大量的对象
- 完全由于使用大量对象造成很大存储开销
- 对象的大多数状态都可以变为外部状态
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象
- 应用程序不依赖对象标识
结构
以下摘自参考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是同一个对象实例,因此当我们有大量的同类果树时,只需要维护一个实例对象和一个树龄数组从而节省了存储空间。
效果
参考
- 《JAVA与模式》之享元模式
- 《Head First 设计模式》
- 《设计模式》
原标题:设计模式C#实现(十三)——享元模式(蝇量模式)
关键词:C#