【问题标题】:When object A instantiates/aggrgate/acquaint object B, must object A have a field member referencing object B?当对象 A 实例化/聚合/熟悉对象 B 时,对象 A 是否必须具有引用对象 B 的字段成员?
【发布时间】:2019-10-22 01:11:56
【问题描述】:

来自 GoF 的设计模式:

表示部分或聚合关系的对象引用是 由底部带有菱形的箭头线表示。箭头指向 聚合的类(例如,Shape)。没有菱形的箭头线 表示熟人(例如,LineShape 保持对 Color 对象的引用,其他 形状可以共享)。参考名称可能会出现在底座附近以区分 它来自其他参考资料 另一个有用的东西是哪些类实例化 其他人。我们使用带箭头的虚线来表示这一点, 因为 OMT 不支持它。我们称之为“创造” 关系。箭头指向实例化的类。在 图 B.lc,CreationTool 创建 LineShape 对象。

  • 当对象 A 聚合对象 B 时,对象 A 是否必须具有引用对象 B 的字段成员?

  • 当对象 A 认识对象 B 时,对象 A 是否必须有一个引用对象 B 的字段成员?

  • 当对象 A 实例化对象 B 时,对象 A 是否必须具有引用对象 B 的字段成员?

【问题讨论】:

    标签: design-patterns aggregation


    【解决方案1】:

    实例化创建一个对象实例(许多语言为此使用new 关键字),而聚合描述对象之间的关系(已经创建或实例化)。 为避免混淆,我必须指出,此示例中使用的所有术语(如 aggregation)均在 Martin Fowler 的上下文中使用,他引入了与 UML 标准定义不同的定义或措辞。

    从您的图表中:

    聚合

    给出了两个类定义DrawingShape,根据您提供的图表,它们具有称为聚合 的关系,根据定义描述了一个共享 em> 这两个对象的生命周期。这意味着Drawing“包含”任意数量的Shapes,或者更准确地说,Shape Drawing 的一部分。当所有者 (Drawing) 的生命周期结束时,Shape 的生命周期也将结束:

    // The `Shape` class
    class Shape
    {
     ...
    }
    
    // The `Drawing`class that aggregates a single `Shape`
    class Drawing
    {
      // The reference to the instance of `Shape`
      private Shape shape;
    
      // The constructor
      public Drawing() 
      {
        // Create an instance of `Shape`.
        // Because the relationship between `Drawing`and `Shape`is an aggregation the instantiation occurs inside the owners constructor (opposed to outside the owner object).
        this.shape = new Shape();
      }
    }
    

    因为DrawingShape之间的关系是一个聚合,所以Shape类型的实例化发生在内部所有者构造函数(在熟人的情况下与所有者对象之外的对象相反)。

    认识

    图中描绘的另一种关系是熟人熟人存在于LineShapeColor 类型的对象之间。这意味着LineShape 使用 ColorColor 将独立于其拥有的 LineShape 对象。对象CreationToolLineShape 之间的虚线描述了一个实例化(创建)。这意味着CreationTool 创建了LineShape 的实例。这是必需的,因为与 aggregation 相反,acquaintance 描述了两个对象的独立生命周期。 Color 可以在其他 Shape 对象之间共享。这需要 LineShape 的相关对象,Color 对象,在所有者之外(而不是在所有者的构造函数内部,如 聚合 场景):

    // The `LineShape` class
    class Color
    {
     ...
    }
    
    // The `LineShape`class that acquaints or associates with a single `Color`
    class LineShape
    {
      // The reference to the instance of `Shape`
      private Color color;
    
      // The constructor
      public LineShape(Color sharedColorInstance) 
      {
        // Request an instance of `Shape` as constuctor parameter.
        // Because the relationship between `LineShape`and `Color`is an acquaintance the instantiation occurs outside the owners constructor  (opposed to inside the owner object).
        this.color = sharedColorInstance;
      }
    }
    
    
    // The `CreationTool` class that creates an instance of `LineShape 
    // and passes a shared instance of `Color`into the constructor.
    class CreationTool
    {
      Color color = new Color();
    
      // Create the instance of `LineShape` 
      // to satisfy the dashed line (relationship) in the diagramm
      LineShape firstLine = new LineShape(color);
    
      // To show the benefit of acquaintance a second instance of `LineShape` is created
      // using the same `Color` instance
      LineShape secondLine = new LineShape(color);
    
      // When firstLine's lifetime ends, 
      // secondLine still has a valid instance of `Color` 
    }
    

    因为LineShapeColor之间的关系是熟人,所以实例化发生在外部所有者构造函数(相对于内部聚合场景中的所有者对象)。这样,Color 的单个实例就可以在多个所有者之间共享。

    正如您在代码示例中看到的,两个关系(或一般的关系)要求将指向相关对象的引用存储在所属对象中。唯一的区别是查看所拥有对象的创建位置哪里。这种情况将描述关系的特殊形式:相关对象是在所有者外部实例化的(熟人)还是在所有者内部实例化的(聚合)? 这意味着您可以通过查看构造函数(或实例化)来区分这两种类型的关系:是传递给构造函数的相关对象实例还是所有者的 setter 方法(熟人)还是所有者的构造函数无参数或无设置器(聚合)?

    对于实例化,字段的要求是另一回事。我们可以说,当CreationTool 实例化LineShape 时,它不需要一个字段来存储对该对象的引用。但是在Color 的情况下,CreationToolobject 可以在字段中存储对Color 实例的引用,以便在创建新的LineShape 实例时重用它(共享它),因为Color 的实例是需要满足LineShape的构造函数。因此,如果需要一个字段来存储对创建者内部已创建实例的引用,这首先是完全可选的,并且取决于上下文。

    此时应该提到,如果是熟人,另一种“注入”拥有的对象实例的方法是使用setter方法:

    Color color = new Color();
    LineShape shape = new LineShape();
    shape.SetColor(color);
    

    尽可能使用构造函数。

    另一个说明,只是为了使它更完整:当用于实现这种关系的语言具有自动内存管理(垃圾收集)时,生命周期控制方面就不再相关了。在 M. Fowlers 世界(或在 UML 世界中的 聚合)中,一切都变成了熟人,因为只要有任何引用存储到拥有的对象实例(例如,当暴露实例通过 getter 方法),垃圾收集器不会破坏此实例,它将继续存在 - 独立于所有者。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-05
      • 1970-01-01
      • 2014-11-25
      • 1970-01-01
      相关资源
      最近更新 更多