Menu Icon Close
App Development

Class Inheritance Sucks, Alternative Exists!

Blog: Alternative to Class Inheritance
February 11, 2014
By Blue Pi
Reading Time: 5 minutes

I’ve been professionally developing OO applications for little over a decade now, and I’m yet to come across a seasoned developer who loves inheritance. Every time this topic is brought up, almost all my programmer buddies have a horror tale to share, or are so overwhelmed with the sad memories it brings to their hearts that all they can muster is a wry smile before changing the topic to something more fruitful and less controversial. So, if inheritance is so bad, why don’t programmers just banish it once and for all. Why don’t we stop using it completely? Is there another alternative that can save us from the dreadful days of debugging through an endless sea of hierarchical classes in a medium-to-large scale project?
Well, lo and behold! There is indeed an alternative. We’ll talk about it in a bit.

If It Is So Bad, Why Do We Still Use It?

Let’s dig a little deeper and look for an answer as to why programmers take to inheritance so naturally. And why did all of us OO programmers, stick to inheritance through a major part of our learning years? Let’s list down the reasons, no matter how whacky they may sound!

  1. Real World vs OO World: Inheritance is one of the basic tenets of the real world, and any OO language would be incomplete if it does not allow a way to map real world’s hierarchical classifications to be represented in the world of code. This is a good reason, and one argument in the favor of inheritance that you can never win. However, one thing I would like to emphasize here is that all objects in the real world can often be classified into multiple classifications based on the context. For example, dogs can be classified as canine creatures in one context, domestic animals in another, and living beings in yet another context.
  2. Code Reusability: Inheritance, in real world as well as Object Oriented principles, implies an inheritance of properties and behavioral characteristics from the parent. However, in its implementation, it inherits code! This undeniable fact is often misinterpreted by young and enthusiastic programmers as a promotion of code reusability. In other words, inheritance is good because it helps in minimizing code duplication. The sad part is that this misinterpretation is rarely corrected and most of us keep repeating the same mistake over and over again, that of using code reusability as the motivation behind using inheritance.

Code Examples Speak Louder Than Words

I can go on babbling about the weaknesses of inheritance, but nothing can help drive the point home other than a good code example. Let’s start with a simple parent class Mammal, that has 2 child classes that inherit from it – Human and Dog.

// -----------------------------------------------
interface IAmMammal
{ void Walk(); void Breathe();
}
// -----------------------------------------------
abstract class Mammal : IAmMammal
{ public abstract void Walk(); public void Breathe() { Console.WriteLine(“Breathing...”); }
}
// -----------------------------------------------
class Human : Mammal
{ public void Walk() { Console.WriteLine(“Walking on 2 legs...”); }
}
// -----------------------------------------------
class Dog : Mammal
{ public void Walk() { Console.WriteLine(“Walking on 4 legs...”); }
}
// -----------------------------------------------

Now, what’s wrong with the above implementation, one may ask. Well, nothing much I say! However, once you start scaling this pattern for larger problems, it can easily become a nightmare. Let’s see how? Let’s say we would like to extend the Human class further to denote Man and Woman.

// -----------------------------------------------
class Man : Human
{
}
class Woman : Human
{
}
// -----------------------------------------------

I’m pretty sure we’re not sweating yet. No harm done, so let’s keep going. As we all know, we humans love to talk! Not so sure about dogs though, so we’d like to add a Talk() method only to Human class, and leave Dog class alone for now.

Hierarchical Implementation

// -----------------------------------------------
interface IAmMammal
{ void Walk(); void Breathe();
}
interface ICanTalk
{ void Talk();
}
// -----------------------------------------------
abstract class Mammal : IAmMammal
{ public abstract void Walk(); public void Breathe() { Console.WriteLine(“Breathing...”); }
}
// -----------------------------------------------
class Dog : Mammal
{ public void Walk() { Console.WriteLine(“Walking on 4 legs...”); }
}
// -----------------------------------------------
class Human : Mammal, ICanTalk
{ public void Walk() { Console.WriteLine(“Walking on 2 legs...”); } public void Talk() { Console.WriteLine(“blah-blah”); }
}
// -----------------------------------------------
class Man : Human
{ public void Talk() { Console.WriteLine(“Howdy!”); }
}
class Woman : Human
{ public void Talk() { Console.WriteLine(“Hello!”); }
}
// -----------------------------------------------

As we can see, we’ve not even started and we’re already dealing with a mess here – hierarchy of 4 classes, 2 interfaces and different levels of method implementations. We already know deep inside our hearts that this is not going to end well. The above pattern is going to be hard to manage, hard to debug and hard to test. Not sure about you, but I’m for sure feeling the heat!

Alternate Implementation

So, let’s see if we could’ve done better. Here’s an alternative implementation involving no hierarchies whatsoever.

I Can Walk

// -----------------------------------------------
interface ICanWalk
{ void Walk();
}
class WalkOn2Legs : ICanWalk
{ Console.WriteLine(“Walking on 2 legs”);
}
class WalkOn4Legs : ICanWalk
{ Console.WriteLine(“Walking on 4 legs!”);
}
// -----------------------------------------------

I Can Breathe

// -----------------------------------------------
interface ICanBreathe
{ void Breathe();
}
class BreatheUsingLungs : ICanBreathe
{ public void Breathe() { Console.WriteLine(“Breathing using my lungs...”); }
}
// -----------------------------------------------

I Can Talk

// -----------------------------------------------
interface ICanTalk
{ void Talk();
}
class ManTalk : ICanTalk
{ public void Talk() { Console.WriteLine(“‘Sup Bro!”); }
}
class WomanTalk : ICanTalk
{ public void Talk() { Console.WriteLine(“Hello!”); }
}
// -----------------------------------------------

I Am Alive

// -----------------------------------------------
interface IAmMammal : ICanWalk, ICanBreathe
{
}
class Mammal : IAmMammal
{ ICanWalk _walkingStyle; ICanBreathe _breathingStyle; public Mammal(ICanWalk walkingStyle, ICanBreathe breathingStyle) { _walkingStyle = walkingStyle; _breathingStyle = breathingStyle; } public void Breathe() { _breathingStyle.Breathe(); } public void Walk() { _walkingStyle.Walk(); }
}
// -----------------------------------------------

I Am Human

// -----------------------------------------------
interface IAmHuman : IAmMammal, ICanTalk
{
}
class Human : IAmHuman
{ IAmMammal _mammal; ICanBreathe _talkingStyle; public Human(IAmMammal mammal, ICanTalk talkingStyle) { _mammal = mammal; _talkingStyle = talkingStyle; } public void Talk() { _talkingStyle.Talk(); } public void Walk() { _mammal.Walk(); } public void Breathe() { _mammal.Breathe(); }
}
// -----------------------------------------------

I Am Dog

// -----------------------------------------------
interface IAmDog : IAmMammal
{
}
// -----------------------------------------------

Now let’s construct the required objects using the new implementation.

// -----------------------------------------------
var dog = new Mammal(new WalkOn4Legs(), new BreatheUsingLungs());
var human = new Mammal(new WalkOn2Legs(), new BreatheUsingLungs());
var man = new Human(human, new ManTalk());
var woman = new Human(human, new WomanTalk());
// -----------------------------------------------

What the F***?

So, now you might be asking one or all of the following questions:

  1. What the hell just happened?
  2. Did he make a simple hierarchical implementation unnecessary complicated?
  3. What do I gain out of this zero-hierarchy mess of classes and interfaces?

Let Me Explain

The alternate implementation above uses what we call strategy pattern, whereby we inject a particular strategy or behavior while constructing an object, and that behavior in turn defines how the object will behave. For example, we know that dog is a mammal that walks on 4 legs, and uses its lungs to breathe. Similarly, human is a mammal that walks on 2 legs, and uses its lungs to breathe. This is exactly what the following 2 lines represent:

// -----------------------------------------------
var dog = new Mammal(new WalkOn4Legs(), new BreatheUsingLungs());
var human = new Mammal(new WalkOn2Legs(), new BreatheUsingLungs());
// -----------------------------------------------

On similar lines, we know that both men and women are 2-legged mammals with different talking styles. And here’s how we construct the appropriate objects:

// -----------------------------------------------
var man = new Human(human, new ManTalk());
var woman = new Human(human, new WomanTalk());
// -----------------------------------------------

But Why?

The beauty of the alternate solution lies in the fact that it clearly keeps all the individual behaviors (like walking, talking, breathing) totally de-coupled from each other as well from the classes that use these behaviors (like Mammal, Human). This allows all these individual classes to be fully functional standalone components, that can be tested, used (or re-used), and extended independently. Personally, I love the fact that I can completely unit-test all those components with very simple unit-tests using a good mocking framework. I’ll talk about that in a subsequent blog post.

TAGS :

Invest in better
solutions today

Contact Us