Composition over inheritance is recently animating the debate on the web. In the previous session, we talked about the the SOLID principles, and also about Liskov:
LSP – Liskov Substitution Principle: inherited objects must have the same properties of the main object (and each property the same characteristics). Can Rectangle inherits from Square? And Square from Rectangle? The answer is use a common base class: Rectangle ← Shape, Square ← Shape [jump to the video]
let’s see how composition could fit to this principles.
Here you have a classic example about inheritance:
We face the first issues with the Kiwi, a cute bird from New Zeland that is know for his inability to fly. We also have issues with the Bat, that can fly but he’s inheriting from Mammal, who can’t.
What about moving the fly method out of Bird, and put it directly to each animal who can fly?
This will introduce lots of duplication, that it’s evil.
Another solution is to put the fly method inside the Animal, in order to avoid code duplication. This exposes all animal to it, also the ones who shouldn’t be able to fly.
So, until now we saw how using inheritance leads to issues that produces code hard to maintain. Let’s try to structure the animal in a different way, to compose them.
Swift allows you to use Protocols, something similar to interfaces in Java. The ability to fly moves from the Animal subclasses to the protocol. Also, with Swift 2 you have the ability to add protocol extensions, that can be used to define default behaviours. In this case, composition helps to assign the ability to fly just to the animals that needs it. That’s sound reasonable, but do we really need all these classes that defines all animals in the entire Earth? Can’t we try to extracts the abilities they have and build different animals that could share same abilities?
The idea is to create as much protocols you need: in our case, a Mover (for flying, walking, etc.) and a SoundMaker (for animal noises). The animals can now be initialised with a name, a soundMaker and mover during initialisation. This is composition: instead of trying to fit all the abilities of a class or object into inheritance, we now during instantiation inject abilities themselves, so we can put together our birds, swifts or dogs objects without having a class that defines them.
In the previous example we got rid of inheritance at all, but we can bring back subclasses in a useful way. Inheritance is used basically for two purposes:
- taxonomy: a mammal is an animal, a dog is mammal, etc.
- factories: create factory methods with standard behaviours
This is the final example. The code is basically the same, but we created something that has nothing to do with animals, even if we can still use the protocols created in precedence.
Breaking up stuff in fine grained protocols rather than rusty inheritance hierarchies gives us code that is not only easy to maintain, but also very likely easy to be reused.
It’s hard to specify rules for composition over inheritance. Enthusiasts would say to start always from composition and avoid inheritance. The truth is that inheritance is useful when defining relationships, but doesn’t make sense for abilities, code with logic.
In general, for taxonomy approach is fine using inheritance, but abilities and logic should use the composition approach.
Thanks again to vikingosegundo that manage to prepare these nice snippets.