Why should one prefer composition before inheritance?

Lecture



Composition preference over inheritance (or the principle of compound reuse) in object-oriented programming (OOP) is the principle that classes must achieve polymorphic behavior and code reuse through their composition (by including instances of other classes that implement the desired functionality), and no inheritance from base or parent class. This is often the proclaimed principle of OOP, for example, in the famous book “Design Patterns”.

Introduce
Implementing composition on top of inheritance usually begins with the creation of various interfaces representing the behavior that the system should exhibit. Using interfaces allows you to maintain polymorphic behavior, which is so valuable in object-oriented programming. Classes that implement the identified interfaces are created and added to the business domain classes as needed. Thus, the system behavior is implemented without inheritance.

In fact, business domain classes can be base classes without any inheritance. An alternative implementation of the system behavior is achieved by providing another class that implements the desired behavior interface. Any class of business domain that contains an interface reference can easily support any implementation of this interface, and the choice can even be postponed until runtime.


Benefits
Composition preference over inheritance is the design principle, which gives the system more flexibility. It is more natural to build classes of business domains from different components than to try to find similarities between them and create a family tree. For example, the gas pedal and the wheel have very few features in common, but both of them are vital components in a car. What they can do and how they can be used for the benefit of the car is easy to determine. The composition also provides a more stable business domain in the long run, as it is less susceptible to the quirks of members of the hierarchy. In other words, it is better to compose what an object can do (HAS-A) than to expand what it is (IS-A).

The original architecture is simplified by identifying the behavior of system objects in separate interfaces instead of creating hierarchical relationships for the distribution of behavior between classes of business domains through inheritance. This approach more easily takes into account future changes in requirements that would otherwise require a complete restructuring of the classes of business domains in the inheritance model. In addition, it avoids the problems often associated with relatively small changes in the model based on inheritance, which includes several generations of classes.

Some languages, especially Go, use only type composition.

Disadvantages
One common disadvantage of using composition instead of inheritance is that the methods provided by the individual components can be implemented in a derived type, even if they are only transfer methods. In contrast, inheritance does not require re-implementation of all the methods of the base class in the derived class. Rather, the derived class should only implement (override) methods that have a different behavior than the methods of the base class. This may require much less programming effort if the base class contains many methods that provide default behavior, and only some of them need to be redefined in the derived class.

This disadvantage can be avoided using traits, mixins, or protocol constructs. Some languages, such as Perl 6, provide handles with a keyword to facilitate the transfer of methods. In the Java Project, Lombok allows you to implement delegation using the single @Delegate annotation for a field, rather than copying and maintaining the names and types of all methods from the delegated field. In Swift, extensions can be used to determine the default implementation of a protocol on the protocol itself, and not in a single type implementation. In Kotlin, the delegation pattern was included in the syntax of the language.

Each option leads to the same end result, with the connection changing from very high (during inheritance) to very weak (during aggregation).

Why should one prefer composition before inheritance?

Conclusion
There are several fairly objective criteria for determining design coherence from 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 amount of inheritance suggests that the designers forgot about the good old advice of the Gang of Four, which boils down to the fact that aggregation of inheritance should be preferred, since the former gives great flexibility and dynamism during execution.

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


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