Favor composition over inheritance
Practical example
Imagine that you are building a game and you have these three beasts in the game.
- phoenix
- pegasus
- dragon
- 1, 2 and 3 can
fly
. - 1 and 2 are
herbivorous
. - 2 and 3 can be
ridden
. - 1 and 3 can
explode
.
We can see how it can get really tricky to use object inheritance to define the correct behavior that each final object needs to have. But it is pretty straightforward with Composition.
- The phoenix implements the
fly
,herbivorous
andexplode
traits. - The pegasus implements the
fly
,herbivorous
andridden
traits. - The dragon implements the
fly
,ridden
andexplode
traits. - All of them are happy.
Composition or inheritance.. which class design is better?
Like a lot of things in life, it depends…
Code re-use in java can be done in two ways
Inheritance
Pros
- Allows polymorphic behavior.
- Is initially simple and convenient.
Cons
- May become complex or clumsy over time if more behavior and relations are added.
Composition
Pros
- Maps well to non-oop scenarios like relational tables, structured programing, etc
- Is straightforward (if not necessarily convenient) to incrementally extend relations and behavior.
Cons
- No polymorphism therefore it’s less convenient to use related information and behavior
How to decide
“if you have an is-a relationship, use inheritance. If you have a has-a relationship, use composition”.
https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
Prefer composition over inheritance as it is more malleable / easy to modify later, but do not use a compose-always approach. With composition, it’s easy to change behavior on the fly with Dependency Injection / Setters. Inheritance is more rigid as most languages do not allow you to derive from more than one type. So the goose is more or less cooked once you derive from TypeA.
My acid test for the above is:
-
Does TypeB want to expose the complete interface (all public methods no less) of TypeA such that TypeB can be used where TypeA is expected? Indicates Inheritance.
- e.g. A Cessna biplane will expose the complete interface of an airplane, if not more. So that makes it fit to derive from Airplane.
-
Does TypeB want only some/part of the behavior exposed by TypeA? Indicates need for Composition.
- e.g. A Bird may need only the fly behavior of an Airplane. In this case, it makes sense to extract it out as an interface / class / both and make it a member of both classes.
The answer is incomplete without a specific mention of Barbara Liskov's Liskov Substitution Principle
as a test for Should I be inheriting from this type?
http://en.wikipedia.org/wiki/Liskov_substitution_principle
There is no One size fits all…
Trying to compare Composition and Inheritance without context is similar to saying “prefer potatoes over coca-cola”. There are places for inheritance and places for composition. We need to understand difference. What it really means is “if you are going to use inheritance - think again, chances are you need composition”.
We should prefer potatoes over coca cola when we want to eat, and coca cola over potatoes when we want to drink.
Creating a subclass should mean more than just a convenient way to call superclass methods. We should use inheritance when subclass “is-a” super class both structurally and functionally, when it can be used as superclass and we are going to use that. If it is not the case - it is not inheritance, but something else. Composition is when our objects consists of another, or has some relationship to them.
Look at the benefits of inheritance: Inheritance
If code reuse is the sole purpose, subclassing may give one more than what he/she needs, i.e. some public methods of the parent class don’t make much sense for the child class. In this case, instead of favoring composition over inheritance, composition is demanded. This is also where the “is-a” vs. “has-a” notion comes from.
So only when subtyping is purposed, i.e. to use the new class later in a polymorphic manner, do we face the problem of choosing inheritance or composition. This is the assumption that gets omitted in the shortened idiom under discussion.
To subtype is to conform to a type signature, this means composition has always to expose no less amount of APIs of the type. Now the trade offs kick in:
- Inheritance provides straightforward code reuse if not overridden, while composition has to re-code every API, even if it’s just a simple job of delegation.
- Inheritance provides straightforward open recursion via the internal polymorphic site
this
, i.e. invoking overriding method (or even type) in another member function, either public or private (though discouraged). Open recursion can be simulated via composition, but it requires extra effort and may not always viable(?). This answer to a duplicated question talks something similar.- https://en.wikipedia.org/wiki/This_%28computer_programming%29#Open_recursion
- https://softwareengineering.stackexchange.com/questions/35946/is-it-bad-code-smell-if-private-method-calls-public-one
- https://github.com/akottr/edu-pattern/blob/master/org.akottr.patterns.composition/src/org/akottr/patterns/composition/inheritance/Compositon.java
- https://stackoverflow.com/a/2238735/2073130
- Inheritance exposes protected members. This breaks encapsulation of the parent class, and if used by subclass, another dependency between the child and its parent is introduced.
- Composition has the befit of inversion of control, and its dependency can be injected dynamically, as is shown in decorator pattern and proxy pattern.
- Composition has the benefit of
combinator-oriented programming
, i.e. working in a way like the composite pattern. - Composition immediately follows programming to an interface.
- Composition has the benefit of easy multiple inheritance.
With the above trade offs in mind, we hence prefer composition over inheritance. Yet for tightly related classes, i.e. when implicit code reuse really make benefits, or the magic power of open recursion is desired, inheritance shall be the choice.
Design principle - HAS-A is better than IS-A
(See Strategy pattern for more context)
Design principle: Favor composition over inheritance
The entire family of behavior patterns revolves around the principle of object composition rather than inheritance
. This principle is currently been widely adopted by the developer community because of the benefits it offers. What this principle states is – instead of extending from an existing class, design your class to refer an existing class that you want to use.
Advantages of Composition over Inheritance
Trying to re-think our problems in terms of composition, in some cases, tends to lead to smaller, simpler, more self-contained, more reusable classes, with a clearer, smaller, more focused scope of responsibility.
Flexibility
One of the primary advantages of composition over inheritance is that it provides greater flexibility in designing and implementing software systems. With composition, we can create complex objects by combining simple objects in different ways, depending on our needs. This means that we can build more modular and reusable code that can adapt to different requirements without the need for extensive modification.
Code Reusability
Another advantage of composition over inheritance is that it provides greater code reusability. By using composition, we can create simple, reusable objects that can be combined to create more complex objects. This means that we can avoid duplication of code, reduce the size of our codebase, and make our code more modular and maintainable.
Avoiding the “Diamond Problem”
Composition can avoid this problem entirely. Instead of inheriting from multiple classes, a class can use objects of other classes to achieve the same functionality. This approach allows for greater flexibility, as the class can dynamically change its behavior by swapping out the objects (objects implementing a common interface) it uses.
References
- https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
- https://en.wikipedia.org/wiki/Composition_over_inheritance
- https://blogs.oracle.com/javamagazine/post/java-inheritance-composition
- https://springframework.guru/gang-of-four-design-patterns/chain-of-responsibility-pattern/