When object A instantiates/aggrgate/acquaint objec

2020-02-13 06:21发布

问题:

From Design Pattern by GoF:

An object reference representing a part-of or aggregation relationship is indicated by an arrowheaded line with a diamond at the base. The arrow points to the class that is aggregated (e.g., Shape). An arrowheaded line without the diamond denotes acquaintance (e.g., a LineShape keeps a reference to a Color object, which other shapes may share). A name for the reference may appear near the base to distinguish it from other references Another useful thing to show is which classes instantiate which others. We use a dashed arrowheaded line to indicate this, since OMT doesn't support it. We call this the "creates" relationship. The arrow points to the class that's instantiated. In Figure B.lc, CreationTool creates LineShape objects.

  • when object A aggregates object B, must object A have a field member referencing object B?

  • when object A acquaints object B, must object A have a field member referencing object B?

  • when object A instantiates object B, must object A have a field member referencing object B?

回答1:

Instantiation creates an object instance (many languages are using the new keyword for this) while aggregation describes the relationship between objects (that are already created or instantiated). To prevent confusion I have to point out that all terms used in this example like aggregation are used in the context of Martin Fowler, who has introduced a different definition or phrasing in contrast to the UML standard definition.

From your diagram:

Aggregation

given are the two class definitions Drawing and Shape that have, according to your provided diagram, a relationship which is called aggregation, which by definition describes a shared lifetime of those two objects. This means a Drawing 'consists' of an arbitrary number of Shapes or to be more precise a Shape is part of a Drawing. When the lifetime of the owner (Drawing) ends, then also the lifetime of Shape will end:

// 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();
  }
}

Because the relationship between Drawingand Shapeis an aggregation the instantiation of the type Shape occurs inside the owners constructor (opposed to outside the owner object in case of acquaintance).

Acquaintance

The other relationship that is pictured by the diagram is the acquaintance. Acquaintance exist between the object of type LineShape and Color. This means a LineShape uses a Color. Color will live independent from its owning LineShape object. The dashed line between the objects CreationTool and LineShape describes an instantiation (create). This means that CreationTool creates the instance of LineShape. This is required since opposed to aggregation acquaintance describes an independent lifetime of both objects. Colorcould be shared between other Shape objects. This requires the related objects of LineShape, the Color object, to be instantiated outside the owner (and not inside the owner's constructor like in an aggregation scenario):

// 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` 
}

Because the relationship between LineShapeand Coloris an acquaintance the instantiation occurs outside the owners constructor (opposed to inside the owner object like in an aggregation scenario). This way a single instance of Color could be shared among multiple owners.

As you can see in the code examples both relations (or relations in general) require the reference, pointing to the related object(s), to be stored inside the owning object. The only difference is when looking at where the owned object was created. This circumstance will describe the special form of the relationship: was the related object instantiated outside the owner (acquaintance) or was it instantiated inside the owner (aggregation)? This means you can distinguish this two types of relationship by looking at the constructor (or instantiation): is the related object instance passed to the constructor or a setter method of the owner (acquaintance) or is the owner's constructor parameter-less or setter-less (aggregation)?

For instantiation the requirement of a field is a different story. We can say that when CreationTool instantiates LineShape it does not need a field to store a reference to this object. But in case of the Color the CreationToolobject can store the reference to the Color instance in a field in order to reuse it (share it) when creating new LineShape instances, since an instance of Color is needed to satisfy the constructor of LineShape. So if a field to store the reference to the created instance inside the creator is required is totally optional in first place and depends on the context.

It should be mentioned at this point, that in case of acquaintance, another way to 'inject' the owned object instance is to use a setter method:

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

Using the constructor should be the prefered way whenever possible.

Another note, just to make it more complete: when the language used to implement such relationships has automatic memory management (garbage collection), then the aspect of lifetime controlling is no more relevant. Everything becomes acquaintance in M. Fowlers world (or aggregation in the UML world), since as long as there is any references stored to the owned object instance (e.g. when exposing the instance via a getter method), the garbage collector won't destruct this instance and it will continue to live - independent from the owner.