We quite got used to inheritance. This pattern is so common you probably use it every single application. The idea behind this pattern is simple: you may extend some object (class, type — whatever is more comfortable in your favorite language) simply inherit methods and properties from “parent” object (class, type) without duplicating its code.
Let’s say we have class “Animal”. This guy has several properties describing him: age, skin color, maybe nickname if it is a pet. Also, it has few methods like “lay”, ”stay”, “run” etc.
(Pay attention, that we usually use nouns for properties and verbs for methods)
Also imagine we have several other guys: fox, rabbit, dog. All of them are animals, in terms that they can act as animals. They can run, stay and lay as an animal. So we can say that all of them inherit these methods from parent object: Animal
Moreover, each of child object/class may have their own methods, not inherited from parent object/class. Let’s say Dog can bark(not all animals can do that), Rabbit can leap (once again — not all animals can afford that) and so on and so far.
This is very common conception and we really got used to it. But there is a problem with it and I’ll try to illustrate it.
How do you think, can animals swim? Well, some of them definitely can! In our case evidently, a dog can do that. Foxes are also capable to do so even though it’s not their favorite thing. What about rabbits? Well, this adorable video proves they can as well!
So we can simply add this kind of behavior to parent animal class because ALL animals can swim:
But… This is not true… Not all animals can swim… Let’s say we have object “Camel”. This guy knows how to lay, how to stay but nothing about swim! Oh… Does it mean camel is not animal? No, it just means that he can not swim and nothing more.
Well, we can create some class AnimalThatCanSwim, inherit it from Animal and then let classes of animals that actually can swim inherit from AnimalThatCanSwim. Yep, that should do the trick:
Alright, that was close. For a second I thought we had a problem!
Ok, now we have a request for another object: Turtle. Well, this guy can swim so let’s add him to a company with Rabbit, Fox, Dog:
But… Turtles can’t run… Ohhh, we need to change our architecture but hopefully just a bit. We will create another class AnimalThatCanRun and inherit Rabbit, Fox, Dog, and Camel from it. Also, Rabbit, Fox, and Dog will inherit from AnimalThatCanSwim.
Now, that should work! But wait… Turtle still can run but it can not in real life… I actually need here multiple inheritances but in most languages, we have no such thing because of “diamond problem”… I need something like this:
Ok, that’s become too complicated. Let’s forget about the turtle for a while. I have another request actually. I need to implement Eagle… Ok, this guy can fly, so I’ll create object AnimalThatCanRunAndFly… But eagles cannot run! Oh, my!
Wait for a second, I know what to do with all this!
I will create Magic Animal — class that has every single possibility at once! In my child classes, I simply remove (override to empty) methods I don’t need!
I think we all agree that this is not a solution. Our Animal becomes a HUGE class and now contains every single option. This case is very hard to maintain.
The main problem here is that our thinking was wrong from the beginning. Instead of thinking “what object might to inherit from” we should concentrate our thoughts on “how our object need to behave?
If we think in that way we may realize, that Rabbit can lay, stay, run and swim. So basically we need some components that add some extension to our Rabbit so he becomes “runnable”, “swimmable”, “layble” etc:
Similarly for Fox and Dog. For Camel this idea should look like this:
For Turtle and Eagle:
This pattern is called Composition. In a bunch of situations, it gives you flexibility and reduces the complexity of the code.
In our case, you are able to add a lot of animals and that won’t ruin your architecture. For example, you may add a Duck, who can run, stay, lay, swim and fly (lucky!); or a Shark who can only swim. Also, you are able to add new behaviors that will extend your object (for instance you might create “can bite” behavior and add to Dog, Fox, and Shark.
Please pay attention, that we used an adjective for names of behaviors. This should highlight the idea that Composition helps you add “abilities” to each class/object.
All that said doesn’t mean inheritance is bad and also doesn’t mean Composition is a “silver bullet”. Every tool has its strengths and weaknesses. Please refer to this article for more information and “edge cases”.