Hello!

I always believed that aggregation is a synonym for composition, however, I came across a blog on the Internet, where differences between composition and aggregation are given .

It blew me away. Please explain the pros / cons of both of these with small examples. How does this affect extensibility, testability, etc.

  • one
    It is explained in sufficient detail. You better write specific questions. - andreycha
  • Yes, there’s really nothing to ask so immediately ... it would be nice if someone explained the pros / cons of both of them with small examples ... how does this affect extensibility, testability, etc. - xperious

1 answer 1

There are several types of interaction of objects, united under the general concept of "Has-A Relationship" or "Part Of Relationship". This relationship means that one object is part of another object.

There are two subspecies of this relationship: if one object creates another object and the lifetime of the "part" depends on the lifetime of the whole, then this is called "composition", if one object receives a link (pointer) to another object in the design process, then this aggregation.

Let's look at an example from the .NET Framework to see what restrictions / consequences this relationship StringWriter : StringWriter + StringBuilder .

The StringWriter class is a specialized version of the TextWriter class that is used extensively in serialization and to get a textual representation of objects.

Specifically, a StringWriter creates a string representation of an object or object graph and relies on an instance of StringBuilder in its work. Those. we can say that 'StringWriter HAS is a StringBuilder' or 'StringBuilder is part of StringWriter'. Now let's see how to decide whether a StringWriter receive an instance of a StringBuilder externally or create it?

On the one hand, for us, as a client of the StringWriter class, StringWriter often doesn’t matter what exactly is used inside this class to get a string representation. This means that in terms of ease of use, it is better that StringWriter create an instance of StringBuilder itself.

But, on the other hand, a specific StringWriter object can only be responsible for obtaining parts of the string representation, and another part of the string can be calculated in another way. From this point of view, it is better that StringWriter take an instance of StringBuilder in the constructor. The same is true for highly loaded systems in which the use of a pool of objects is reasonable.

Since StringWriter is a library class that should support both scripts, it has overloaded constructor versions: one of them creates a StringBuilder inside, and the other accepts it outside.

In other words, the choice between composition and aggregation is based on respect for the balance between the various design requirements:

Composition: object A controls the lifetime of object B

Pros:

  • Composition allows you to hide the use of objects from the eyes of the client.

  • It makes the class usage API simpler and allows you to switch from using one class to another (for example, a StringWriter could change the implementation and start using another type, for example, CustomStringBuilder ).

Minuses:

  • The relationship is quite tough, since one object must be able to create another: it must know the specific type and have access to the creation function. The composition does not allow the use of interfaces (without the involvement of factories) and requires that the class have access to the constructor of another class: imagine that the StringBuilder constructor is internal or is it the IStringBuilder interface and only the client code knows which instance should be used here and now.

Aggregation: object A receives a reference to object B

Pros:

  • A weaker connection between an object and its client. Now we can use interfaces and one object does not need to know how to create another object.

  • Greater flexibility. Derives from the first item

Minuses:

  • Exposure of implementation details. Since the class client must provide a dependency at the time of creating the object (pass an instance of StringBuilder at the time of creating the StringWriter , the fact of this relationship becomes known to the client.

  • The first point implies an increase in the complexity in the work of clients, as well as a large “rigidity” of the solution in the long term. Now the author of the TextWriter class TextWriter no longer decide on its own and move from StringBuilder to something else. Yes, you can "add another level of abstraction" and highlight the IStringBuilder interface, but it will be impossible to break this relationship completely without breaking all existing customers.

In conclusion: design is a search for a compromise between various factors. The composition is simpler from the point of view of the clients of the class, but it imposes certain restrictions: the "whole" must be able to create the "integral part". Aggregation is more flexible, but imposes other restrictions: now the "whole" does not hide the existence of a "component", and therefore cannot replace it with another "component" in the future.

PS If you really want to use an example from the real world, then a screwdriver may be appropriate to explain the composition and aggregation. If the screwdriver is solid, i.e. the handle and the nozzle are tightly connected to each other, then we have the composition relation. If the nozzle is removable and can exist without a handle or used with another handle, then we have an aggregation relationship.

  • well ... a pool of objects, an interface factory ... weakly with theory, thanks - xperious
  • I realized that in order to experience the difference, it is necessary to write something very large; sometimes even a simple choice cannot be solved into small ones: stupid inheritance or aggregation - xperious
  • one
    There is such a thing: in real-life tasks, it’s such that, under normal conditions in life, it will not come to mind. I would even recommend a personal project to do it tightly. In many of my github projects, all this is already needed. Although they are not very big: github.com/SergeyTeplyakov - Sergey Teplyakov
  • And why when you talk about composition, every time you mention the coincidence of the lifetime of the part and the object creating it, and when about aggregation, you don’t say such a thing? It is just that every time you make an emphasis on this, which may give the impression that if during aggregation a link to a part will also coincide in the lifetime with an object that stores a link to this part, then it will not be aggregation anymore. all the same there will be an aggregation? - user220409
  • 3
    @OlmerDale in the case of composition and aggregation - limiting the lifetime - is a key feature. If the whole creates a part and does not let it out, then by that time the life of the part is limited to the whole. In the case of aggregation, the part and the whole are not so tightly connected. In the case of languages ​​with GC, in the case of aggregation, a part and a whole can die at the same time, but this is more a coincidence than a rule. - Sergey Teplyakov Nov.