17

意图:

这种模式的目的是使用共享来支持大量对象,这些对象的内部状态的一部分是相同的,而另一部分的状态可能会有所不同。

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

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

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

4

4 回答 4

17

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

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

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

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

于 2014-05-15T16:43:19.703 回答
8

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

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

使用一些静态成员声明一个非静态类比将整个类声明为静态更典型。静态字段的两个常见用途是对已实例化的对象的数量进行计数,或者存储一个必须在所有实例之间共享的值

MSDN 上的 C# 静态源代码

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

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

于 2014-05-15T17:02:19.333 回答
3

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

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

于 2014-05-15T17:00:54.090 回答
1

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

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

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";
于 2017-11-14T11:20:29.760 回答