【问题标题】:Understanding the Flyweight pattern理解享元模式
【发布时间】:2014-07-04 05:49:57
【问题描述】:

意图:

此模式的目的是使用共享来支持大量 具有部分内部状态共同的对象,其中 州的其他部分可能会有所不同。

对象可以通过静态字段共享状态。

使用享元模式和使用静态字段共享大量对象的内部状态有什么区别?

享元通过其工厂提供的对象池是享元的真正意义所在吗?

【问题讨论】:

    标签: c# design-patterns flyweight-pattern


    【解决方案1】:

    使用静态字段,在任何一个时间点都只能有一个正在使用的对象实例。使用享元模式,您可以同时使用任意数量的不同实例(每个实例都被多次使用)。享元模式的典型示例是文本编辑器,您需要为文档中的每个字符实例化一个对象。无需为 10,000 字文档中的每个字符在内存中存储一​​个对象,您只需 26 个对象(假设文档仅使用小写字母),一个用于字母“a”,一个用于字母“b”,等等.,并且它们在整个文档中一次又一次地临时重复使用,每次您需要执行一些需要“a”对象的功能或动作时。

    编辑:从下面的第一条评论中回答问题:
    因此,由于您需要 26 个不同的对象,因此创建一个静态或单例的 Letter 类是行不通的。如果它是静态的,则无法创建任何实例,因此无论静态值如何,都必须适合代码中使用它的每个位置。 如果它是一个单例,那么你当然只有一个对象。每次使用时,每个属性都必须可调整(和调整)。 要将这种模式用于字母表中的字母,您必须有 26 个不同的类,每个字母一个类...

    此外,“类中可以变化的部分”实际上意味着某些字段表示对于类的每个实例不同的状态。而 共同 的部分意味着这些公共字段的值对于匹配这些状态值的对象的所有用途都是共同的(例如所有的 'a'),而不是对于班级。

    再次以文本编辑器为例。 代码中需要处理“a”字符的每个地方,首先,转到存储 26 个字符对象实例的数据结构,然后获取“a”实例, 您将首先修改/更改 varying 属性(这些属性与它作为“a”的性质无关,但可能与它的字体大小、位置、颜色等有关)以匹配此需求文档中的特定字符“a”。
    然后你可以利用这个对象来做你需要做的任何事情,然后将它返回到存储结构中以供下次代码需要'a'时重用。

    【讨论】:

    • If the letter class was a static, or a singleton, that wouldn't work. 为什么?从您的解释来看,这并不明显。
    • 因此,如果我正确理解了文本编辑器示例:如果我的文档中有 10000 个字母,而不是 10000 个 Letter 对象,我只需对这 26 个 @987654324 中的任何一个有 10000 个引用@对象?
    • 不,您不持有对对象的引用,您只在需要时按需使用 26 个对象。比如yoyu需要画一个有1200个字符的屏幕,你看第一个字符是什么,说它是'a',所以你得到'a'对象,设置它的属性和文档字符一致将暂时表示,使用对象,(可能在屏幕上绘制“a”),然后忘记对象(它仍在 26 字符对象存储中),并迭代到下一个字符。
    • 这是一个很好的例子。一个更复杂的结构化文本示例是在编译器中。在分解程序文本时,您可能希望拥有int;null 等的享元实例,因为您知道您将看到很多这样的实例。跨度>
    【解决方案2】:

    享元模式用于避免大量非常相似的类的开销。在某些情况下,您似乎需要生成大量的小类实例来表示数据。有时,如果您能认识到除了少数参数之外实例基本相同,则可以大大减少需要实例化的不同类的数量。如果您可以将这些变量移出类实例并将它们作为方法调用的一部分传入,那么通过共享它们可以大大减少单独实例的数量。

    在这种情况下,重要的是要记住 Flyweight 是在 C# 只不过是一些简报图表上的粗略草图的时代发明的。其中一些模式隐含地告知了语言的成熟。 C# 包括类成员...

    更典型的做法是声明一个带有一些静态的非静态类 成员,而不是将整个类声明为静态的。的两种常见用途 静态字段用于记录具有的对象数量 被实例化,或存储一个必须在所有人之间共享的值 实例

    来源C# statics on MSDN

    更进一步,WPF 技术普及了共享资源,结果往往只是声明性代码。

    因此,如果您选择的语言是 C#,则建议您针对该语言中已经存在的固有属性考虑享元模式。

    【讨论】:

    • 在所有实例中 - 并非所有实例都需要该值。
    • 据我所知,WPF 的DataTemplateSelector 确实需要过程编码(以覆盖SelectTemplate method)。 MS 没有添加任何以声明方式选择模板的方法,因此(像 WPF 的许多部分一样)选择器仅在使用中声明(实际上是 XAML 中的 one 行),而大部分代码仍然是非声明性的。
    • @O.R.Mapper 是真的,没有声明性选择器。写你自己的。用不了多久。
    • @Gusdor:“不长”是一个观点问题。在 C# 中将 someBool ? "TrueTemplate" : "FalseTemplate" 的逻辑等价物(可能是 4 行 XAML 或更少)编写为 DataTemplateSelector 子类需要相当长的时间(并且包括在代码中检测和加载资源等任务- 只需查看 DataTemplateSelector 文档中相对简单的示例。
    【解决方案3】:

    虽然模式及其实现有点主观,但使用静态是实现享元的有效方法——尽管可能最简单。

    如果你可以使用静态,那就太好了。否则你可以做一些你已经接触过的事情......你构造享元对象的工厂可以分配/引用正确的共享对象。

    【讨论】:

      【解决方案4】:

      这是一个例子,它打印了所有的士兵和他们的勋章。

      因为不是所有的士兵都有装饰,所以我们使用享元设计模式。

      public class Soldiers
      {
        private string[] _soldiers;
        private Dictionary<int, Medal> _medals = new Dictionary<int, Medal>();
      
        public Soldiers(string[] soldier)
        {
          this._soldiers = soldier;
        }
      
        public Medal this[int index]
        {
          get
          {
            Medal medal = new Medal();
            this._medals.Add(index, medal);
            return this._medals[index];
          }
        }
      
        public override string ToString()
        {
          var soldierList = new List<string>();
          for (var i = 0; i < this._soldiers.Length; i++)
          {
            string soldier = this._soldiers[i];
            if (this._medals.ContainsKey(i))
              soldier = soldier + ", Medal: " + this._medals.ElementAt(i).ToString();
            soldierList.Add(soldier);
          }
          return string.Join("; ", soldierList);
        }
      
        public class Medal
        {
          public bool Bronze;
          public bool Silver;
          public bool Gold;
      
          public override string ToString()
          {
            return (this.Bronze ? "Bronze," : "") + (this.Silver ? "Silver," : "") + (this.Gold ? "Gold" : "")
          }
        }
      }
      

      用法:

      Soldiers soldiers = new Soldiers(new string[] { "SoldierA" , "SoldierB" , "SoldierC" });
      
      soldiers[0].Gold = true;
      soldiers[0].Silver = true;
      soldiers[2].Bronze = true;
      Console.Write(soldiers.ToString()) // "SoldierA, Medal: Silver,Gold; SoldierB; SoldierC, Medal: Bronze";
      

      【讨论】:

      • 我不认为这是 Fly-Weight 模式的一个很好的例子。它实际上并没有重用任何实例。事实上,如果我正确阅读代码,如果多次授予相同的 Medal,它实际上会创建重复的 Medal 实例,这正是 Fly-Weight 旨在避免的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-27
      • 1970-01-01
      • 1970-01-01
      • 2016-03-25
      • 2021-12-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多