difference Inheritance between Composition between UML Aggregation

Lecture



There are different types of relationships between two classes / objects. The most basic type of relationship is association , which means that the two classes are somehow related to each other, and we do not yet know exactly what this relationship is expressed in and are going to clarify it in the future. Typically, this relationship is used in the early stages of a design to show that dependency between classes exists, and move on.

  difference Inheritance between Composition between UML Aggregation

Figure 1. Association relationship

A more accurate type of relationship is the open inheritance relationship (the “is” relationship, IS A Relationship), which says that everything that is true for the base class is also true for its successor. It is with its help that we obtain polymorphic behavior, abstracting from concrete implementation of classes, dealing only with abstractions (interfaces or base classes) and not paying attention to implementation details.

  difference Inheritance between Composition between UML Aggregation

Figure 2. The inheritance relationship

And although inheritance is an excellent tool in the hands of any OO programmer, it is clearly not enough to solve all types of problems. Firstly, not all relations between classes are determined by the “is” relationship, and secondly, inheritance is the strongest connection between two classes that cannot be broken during execution (this relation is static and, in strictly typed languages, is determined at compile time ).

In this case, another pair of relationships comes to the rescue: composition and aggregation . Both of them model the relationship “is part” (HAS-A Relationship) and are usually expressed in the fact that the class of the whole contains the fields (or properties) of its constituent parts. The line between them is rather thin, but important, especially in the context of dependency management.

  difference Inheritance between Composition between UML Aggregation

Figure 3. Composition and aggregation ratio

Hint
A couple of points to make it easier to remember the visual notation: (1) the diamond is always on the side of the whole, and a simple line on the part of the component ; (2) a shaded rhombus means a stronger bond — a composition; an unfilled rhombus shows a weaker bond — aggregation.

The difference between composition and aggregation is that, in the case of composition, the whole clearly controls the lifetime of its component (the part does not exist without the whole), and in the case of aggregation, the whole even contains its component, their lifetime is not related (for example, component the part is passed through the constructor parameters).

  class    CompositeCustomService 
  { 
// Composition     private   readonly   CustomRepository _repository
= new   CustomRepository ();
public   void DoSomething ()
{
// Use _repository } }
  class    AggregatedCustomService 
  { 
// Aggregation     private   readonly   AbstractRepository _repository;
public AggregatedCustomService ( AbstractRepository repository)
{
_repository = repository;
}
public   void DoSomething ()
{
// Use _repository } }

CompositeCustomService uses composition for managing its component parts, and AggregatedCustomService for aggregation. In this case, an explicit control of the lifetime usually leads to a higher coherence between the whole and the part, since a particular type is used that closely connects the participants to each other.

On the one hand, such a hard link may not be something bad, especially when the dependency is stable (see the section “Stable and changeable dependencies” in the last note). On the other hand, we can use the composition and control the lifetime of the object, without being tied to specific types. For example, using an abstract factory:

  internal    interface    Irepositoryfactory 
  { 
AbstractRepository Create (); } class   CustomService {
// Composition     private   readonly   IRepositoryFactory _repositoryFactory;
public CustomService ( IRepositoryFactory repositoryFactory)
{
_repositoryFactory = repositoryFactory;
}
public   void DoSomething ()
{
var repository = _repositoryFactory.Create ();
// Use the created AbstractRepository } }

In this case, we do not get rid of the composition ( CustomService still controls the lifetime of the AbstractRepository ), but it does this not directly, but with the help of an additional abstraction — an abstract factory. Since this approach requires doubling the number of classes of our dependencies, it should be used when explicit control of the lifetime is a necessary condition.

An interesting feature of the different relationships between classes is that the consistency of their use may depend on the designer’s point of view, on which side he looks at the task and what questions he asks himself when analyzing it. That is why the same problem can be solved in a dozen different ways, while in one case we will get a strongly connected design with a lot of inheritance and composition, and in the other case the same task will be broken down into more autonomous building blocks that are interconnected with using aggregation.

For example, our task with services and repositories can be solved in many different ways. Someone will say that inheritance is suitable here and make SqlCustomService a successor from AbstractCustomService ; another will say that this approach is incorrect, since we have one CustomService , and the repository should have a hierarchy.

  difference Inheritance between Composition between UML Aggregation

Figure 4. Inheritance vs Aggregation

Each variant leads to the same final result, and the connectivity varies from very high (with inheritance) to very weak (with aggregation).

Conclusion

There are several fairly objective criteria for determining the coherence of a design using a class diagram: large inheritance hierarchies (deep or wide hierarchies), and the widespread use of composition, rather than aggregation, most likely indicates a strongly related design.

A large number of inheritance suggests that the designers have forgotten about the good old advice of the Gang of Four, which boils down to the fact that one should prefer aggregation to inheritance, since the former gives greater flexibility and dynamism during execution.

The abundance of the composition speaks of a violation of the Principle of Inversion of Dependencies formulated by Bob Martin, which can now be expressed in terms of aggregation and composition: prefer aggregation instead of composition , since the former encourages the use of abstractions rather than specific classes.

Next time: let's turn to the consideration of specific DI patterns and start with the most popular of them - Constructor Injection.

created: 2015-05-13
updated: 2021-03-13
132606



Rating 9 of 10. count vote: 2
Are you satisfied?:



Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Object oriented programming

Terms: Object oriented programming