The Story
I recently encountered a question about why it’s better to using interface than inheritance. I have this because we defined lots of interfaces (behaviors) in a recent project. And, I am not saying this is bad, but just because this is not my familiar pattern when trying to design a system and also wanted to think of it deeper why we are doing that. I am confused if some classes share the same behaviors, my first thought from Java world is to create a parent class to define those common behaviors and then derivative classes would get the benefits without implementing the same behaviors again. But someone told me: Using an interface is better. So, I want to figure out why an interface is better than inheritance. According to the wiki description:
Composition over inheritance - Wikipedia
Composition over inheritance (or composite reuse principle) in object-oriented programming (OOP) is the principle that classes should achieve polymorphic behavior and code reuse by their Composition (by containing instances of other classes that implement the desired functionality) rather than inheritance from a base or parent class
Composite Reuse Principle
Yes, that’s also called composite reuse principle. So, the relationships of inheritance and Composition are referred to as IS-A and HAS-A relationships. Then why should we use the composition pattern (Interfaces) more?
- Flexibility of reuse
- More precise and readable logic.
- Easily create test cases for components.
Let’s check an example about the inheritance. You need to remember that’s an IS-A
relationship.
1 | open class Dog { |
open class Dog {
fun bark() {
println(“Bark!”)
}
}
class GermanShephard : Dog() {
fun sniffDrugs(drugs: Objects) {
if (drugs != null) bark()
}
}
class BelgianMalinois : Dog() {
fun sniffDrugs(drugs: Objects) {
if (drugs != null) bark()
}
}
class Poodle: Dog()
1 |
|
open class Dog {
fun bark() {
println(“Bark!”)
}
fun sniffDrugs(drugs: Objects) {
if (drugs != null) bark()
}
}
…
// Does a poodle can do sniffing drug work?
class Poodle: Dog()
1 |
|
open class Dog {
fun bark() {
println(“Bark!”)
}
}
open class SniffTrainingDog: Dog() {
fun sniffDrugs(drugs: Objects) {
if (drugs != null) bark()
}
}
class GermanShephard : SniffTrainingDog() { … }
class BelgianMalinois : SniffTrainingDog() { … }
class Poodle: Dog()
1 |
|
interface Barker {
fun bark()
}
interface Swimmer {
fun swim()
}
interface DrugSniffer{
fun drugSniff()
}
class GermanShephard: Barker, DrugSniffer {
override fun bark() { … }
override fun drugSniff() { … }
}
That would be easier to test and define what a german Shephard can do. Is the inheritance dead, and we can't use it anymore? No, I don't think so. I think we should carefully choose the design pattern before we start a project.
Happy coding, enjoy.
#### [Reference]
1. [Composition over inheritance - Wikipedia](https://en.wikipedia.org/wiki/Composition_over_inheritance)
2. [Composition over Inheritance - Fun Fun Function - Medium](https://medium.com/humans-create-software/composition-over-inheritance-cb6f88070205)
3. [Interfaces vs Inheritance in Swift - Mike Buss](https://mikebuss.com/2016/01/10/interfaces-vs-inheritance/)
4. [Protocol-Oriented Programming in Swift - WWDC 2015 - Videos - Apple Developer](https://developer.apple.com/videos/play/wwdc2015/408/)
5. [OO Design Patterns: Composition Vs. Inheritance - The Startup - Medium](https://medium.com/swlh/oo-design-patterns-composition-vs-inheritance-4206a6b018bb)