享元模式 以共享的方式高效地支持大量的细粒度对象 享元对象的状态 :内蕴状态(Internal State)内蕴状态存储在享元对象内部且不会随环境改变而改变因此内蕴状态并可以共享 :外蕴状态(External State)外蕴状态是随环境改变而改变的不可以共享的状态享元对象的外蕴状态必须由客户端保存并在享元对象被创建之后在需要使用的时候再传入到享元对象内部外蕴状态与内蕴状态是相互独立的 享元模式的应用条件 : 一个系统有大量的对象 :这些对象耗费大量的内存 :这些对象的状态中的大部分都可以外部化 :这些对象可以按照内蕴状态分成很多的组当把外蕴对象从对象中剔除时每一个组都可以仅用一个对象代替 :软件系统不依赖于这些对象的身份换言之这些对象可以是不可分辨的 NET的享元模式 NET中的String类型就是运用了享元模式……NET中如果第一次创建了一个字符串对象s下次再创建相同的字符串s时只是把它的引用指向s所引用的具体对象这就实现了相同字符串在内存中的共享下面的程序来演示s和s的引 用是否一致 输出的结果为True string s = 测试字符串一; string s = 测试字符串一; ConsoleWriteLine(ObjectReferenceEquals(s s)) 注意如果再有一个字符串s它的初始值为测试字符串再对它进行操作s = s + 一这时虽然s和s的值相同但是它们的引用是不同的 享元模式的分类 单纯享元模式 复合享元模式 第一单纯享元模式 单纯享元模式结构图 单纯享元模式构成说明 >:抽象享元(Flyweight)角色此角色是所有的具体享元类的超类为这些类规定出需要实现的公共接口那些需要外蕴状态(External State)的操作可以通过调用商业方法以参数形式传入 /// <summary> /// Flyweight /// </summary> abstract class Flyweight { // Methods /// <summary> /// 抽象享元对象的商业方法 /// </summary> /// <param name=extrinsicstate>外蕴状态</param> abstract public void Operation(int extrinsicstate) } >:具体享元(ConcreteFlyweight)角色实现抽象享元角色所规定的接口如果有内蕴状态的话必须负责为内蕴状态提供存储空间享元对象的内蕴状态必须与对象所处的周围环境无关从而使得享元对象可以在系统内共享的 /// <summary> /// ConcreteFlyweight /// </summary> class ConcreteFlyweight : Flyweight { private string intrinsicstate = A; // Methods override public void Operation(int extrinsicstate) { ConsoleWriteLine(ConcreteFlyweight: intrinsicstate {} extrinsicstate {} intrinsicstate extrinsicstate) } } >:享元工厂(FlyweightFactory)角色本角色负责创建和管理享元角色本角色必须保证享元对象可以被系统适当地共享当一个客户端对象调用一个享元对象的时候享元工厂角色会检查系统中是否已经有一个复合要求的享元对象如果已经有了享元工厂角色就应当提供这个已有的享元对象如果系统中没有一个适当的享元对象的话享元工厂角色就应当创建一个合适的享元对象 注意客户端不可以直接实例化享元类必须通过享元工厂类来创建因为享元工厂类在系统中只能有一个所以可以结合单例模式来改善当客户端需要单纯享元对象时需要调用享元工厂的Singleton()方法此时工厂会取得所有的单纯享元对象然后传入所需的单纯享元对象的内蕴状态工厂方法负责产生所需要的享元对象 /// <summary> /// FlyweightFactory /// </summary> class FlyweightFactory { // Fields private Dictionary<string Flyweight> flyweights = new Dictionary<string Flyweight>() private static readonly FlyweightFactory instance = new FlyweightFactory() /// <summary> /// Constructors /// </summary> private FlyweightFactory() { } // Methods /// <summary> /// 从享元工厂中生产出一个具体的享元对象 /// </summary> /// <param name=key>内蕴状态</param> /// <returns></returns> public Flyweight GetFlyweight(string key) { return ((Flyweight)flyweights[key]) } /// <summary> /// 享元工厂单例方法 /// </summary> /// <returns></returns> public static FlyweightFactory Singleton() { return FlyweightFactoryinstance; } /// <summary> /// 向享元工厂对象增加一个享元对象 /// </summary> /// <param name=sKey>内蕴状态</param> /// <param name=_Flyweight>具体享元对象</param> public void AddFlyweight(string sKey Flyweight _Flyweight) { flyweightsAdd(sKey _Flyweight) } public Flyweight factory(string sKey) { if (flyweightsContainsKey(sKey)) { return thisGetFlyweight(sKey) } else { thisAddFlyweight(sKey new ConcreteFlyweight()) return thisGetFlyweight(sKey) } } } >:客户端(Client)角色需要维护一个对所有享元对象的引用需要自行存储所有享元对象外蕴状态 // 初始化外蕴状态值 int extrinsicstate = ; //享元工厂对象使用单例 FlyweightFactory f = FlyweightFactorySingleton () ; //调用过程 //向享元工厂对象请求一个内蕴状态为X的单纯享元对象 Flyweight fx = ffactory(X) //调用X的商业方法X的外蕴状态值为 fxOperation(extrinsicstate) Flyweight fy = ffactory(Y) fyOperation(extrinsicstate) Flyweight fz = ffactory(Z) fzOperation(extrinsicstate) 第二复合享元模式 复合享元模式结构图 结构对象说明 :抽象享元角色此角色是所有的具体享元类的超类为这些类规定出需要实现的公共接口那些需要外蕴状态(External State)的操作可以通过方法的参数传入抽象享元的接口使得享元变得可能但是并不强制子类实行共享因此并非所有的享元对象都是可以共享的 :具体享元(ConcreteFlyweight)角色实现抽象享元角色所规定的接口如果有内蕴状态的话必须负责为内蕴状态提供存储空间享元对象的内蕴状态必须与对象所处的周围环境无关从而使得享元对象可以在系统内共享有时候具体享元角色又叫做单纯具体享元角色因为复合享元角色是由单纯具体享元角色通过复合而成的 :复合享元(UnsharableFlyweight)角色复合享元角色所代表的对象是不可以共享的但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合复合享元角色又称做不可共享的享元对象 :享元工厂(FlyweightFactoiy)角色本角色负责创建和管理享元角色本角色必须保证享元对象可以被系统适当地共享当一个客户端对象请求一个享元对象的时候享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象如果已经有了享元工厂角色就应当提供这个已有的享元对象如果系统中没有一个适当的享元对象的话享元工厂角色就应当创建一个新的合适的享元对象 :客户端(Client)角色本角色还需要自行存储所有享元对象的外蕴状态 享元模式的优点 大幅度地降低内存中对象的数量 享元模式的缺点 :享元模式使得系统更加复杂为了使对象可以共享需要将一些状态外部化这使得程序的逻辑复杂化 :享元模式将享元对象的状态外部化而读取外部状态使得运行时间稍微变长 总结 享元模式一般是解决系统性能问题的所以经常用于底层开发在项目开发中并不常用 |