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

时间:2019-06-06 13:42:06

标签: design-patterns aggregation

来自GoF的设计模式:

  

表示部分或聚合关系的对象引用为   用箭头表示,底部带有菱形。箭头指向   汇总的类(例如Shape)。没有钻石的箭头线   表示相识(例如LineShape保留对Color对象的引用,而其他   形状可以共享)。参考名称可能会出现在底座附近以区别   从其他参考   另一个有用的事情是实例化   其他的。我们使用虚线箭头表示这一点,   因为OMT不支持。我们称其为“创造”   关系。箭头指向实例化的类。在   图B.lc,CreationTool创建LineShape对象。

     

enter image description here

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

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

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

1 个答案:

答案 0 :(得分:1)

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

从图中:

聚合

根据您提供的图表,给出了两个类定义DrawingShape,它们之间的关系称为 aggregation ,根据定义,该关系描述了共享生存期。这意味着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实例发生在内部 所有者构造函数(如果是相识,则与所有者对象相对)。

相识

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

// 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之间的关系是认识,因此实例化发生在外部所有者构造函数(而不是在 aggregation 场景中在所有者对象内部)。这样,Color的单个实例可以在多个所有者之间共享。

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

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

在这一点上应该提到,对于熟人,“注入”拥有对象实例的另一种方法是使用setter方法:

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

在可能的情况下,首选使用构造函数。

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