« Back to home

No More Inheritance

Let me be honest, I hate inheritance. There's no other programming feature that annoys me as much as having two classes inherit from each other. And it's not just classical inheritance. Prototypal inheritance, which I used to think as a good idea, is not an exception as well. I have now nearly stopped using any kind of inheritance in all projects that I work in, and I hope that I can convince you to do the same.

What's wrong with inheritance

Inheritance creates the "banana-gorilla" problem. If you're trying to acquire a banana, but that banana is being held by a gorilla, and the gorilla, in turn, is holding on to a tree in the jungle, you'll end up getting the entire jungle. That's what inheritance does to your program - classes and modules have access to way more responsibilities than they need. The worst thing is that you can't deny the unwanted inherited properties. The jungle is always there whether you use it or not.

It's also easy to over-engineer when inheritance is present in a programming language. Most of the time in software development, what we need is a concrete implementation of a feature, not a bunch of taxonomies. If the requirement is to build a Dog and a Gorilla class, it's very tempting to create an Animal class that both of them inherit from. If a Human class is added in the future, we may not resist the itch to add an Hominidae abstract class as well. I just want a dog and a gorilla, but now end up with the entire animal kingdom. It is how Object Oriented Programming is taught in school, mainly because it's easier for beginners to relate to real world concepts. But as we mature, it's sad that we're still keeping the early habits and trying to make sense of software systems that way. Inheritance is not meant to achieve code organization and code reuse. It's mainly a polymorphic tool, and a lot of the time, the need for polymorphism arises from our own desire to use it.

I hate it the most when reading code that abuses inheritance. Finding a particular method that handles the behavior I'm looking for requires jumping back and forth between classes in the inheritance chain. I have to keep a mental model of the inherited attributes and keep track of the communication between overridden and inherited methods. It's nearly impossible to do so without a proper IDE. That's really stupid as I should only need an IDE to write code, not read other people's code. The implementation for a functionality I'm looking for should be easy to trace and the responsibilities of a class/module obvious to see. I shouldn't have to search the entire jungle just to look for something inside a banana.

Prototypal inheritance doesn't make things easier either. JavaScript is the only language I know that has prototypal inheritance built in, which is already a huge improvement over classical languages. It eliminates the unnecessary concept of classes and instead relies on constructor functions. However, that in turn makes prototypal inheritance itself very complex. Just take a look at the code snippet below taken from https://developer.mozilla.org/en/docs/Web/JavaScript/Inheritanceandtheprototypechain:

function A(a){
  this.varA = a;
}

A.prototype = {
  varA : null,  
  doSomething : function(){
    // ...
  }
}

function B(a, b){
  A.call(this, a);
  this.varB = b;
}
B.prototype = Object.create(A.prototype, {
  varB : {
    value: null, 
    enumerable: true, 
    configurable: true, 
    writable: true 
  },
  doSomething : { 
    value: function(){ // override
      A.prototype.doSomething.apply(this, arguments); // call super
      // ...
    },
    enumerable: true,
    configurable: true, 
    writable: true
  }
});
B.prototype.constructor = B;

var b = new B();
b.doSomething();

That's a lot of work just for a simple "B inherits from A" implementation. That code is so ugly that every JS library out there would be incomplete if they don't include some syntactic sugar to make it more pleasant. When writing Javascript, I don't really want to use external libraries unless they're absolutely important. In that case, either I have to write the wrapper myself or stay away from prototypal inheritance altogether. I always choose the latter one.

Replacement for inheritance

So what can we do instead of inheritance? You should probably know the answer to this already - Composition. Composition over Inheritance has become a very popular ideology nowadays, but the majority of programmers I know, especially Java programmers, are still unfamiliar with it. The idea is simple. If you have a piece of code that should be shared among objects, extract it to a common module. Any objects that need the functionality can grab the methods from the common module. Let's go back to the Dog and Gorilla example and try to implement it with composition:

public class Dog {
  private Animal animal;

  public Dog(Animal animal) {
    this.animal = animal;
  }  

  public String walk() {
    return animal.walk();
  }

  public String bark() {
    return "Dog is barking";
  }
}

public class Gorilla {
  private Animal animal;

  public Gorilla(Animal animal) {
    this.animal = animal;
  }

  public String stand() {
    return animal.stand();
  }

  public String holdBanana() {
    return "Gorilla is holding a banana. Don't try to get the banana!";
  }
}

public class Animal {
  public String walk() {
    return "walking";
  }

  public String stand() {
    return "standing";
  }
}

The Animal class now becomes just a collection of common animal behaviors. Dog and Gorilla each has an instance of Animal and can define their own behaviors using what it provides. It's a much better approach because now, instead of getting the behaviors directly through inheritance, Dog and Gorilla can declare their own interface and use the general implementation in Animal if needed. We don't have to jump back and forth in the inheritance chain to tell what the responsibilities of the classes are. It may not fit the Has-A and Is-A relationship model that OOP design promotes, but that shouldn't matter. Software and code is not real world. Stop trying to force real world common sense onto it.

The same idea can be easily applied to other languages. In Javascript, this is 1 way to implement this example:

function animalConstructor() {
  return Object.freeze({
    walk: function() {
      return "walk";
    },

    stand: function() {
      return "stand";
    }
  });
}

function dogConstructor(animal) {
  return Object.freeze({
    bark: function() {
      return "Dog is barking";
    },

    walk: function() {
      return animal.walk();
    }
  });
}

function gorillaConstructor(animal) {
  return Object.freeze({
    holdBanana: function() {
      return "Gorilla is holding a banana. Don't try to get the banana!";
    },

    stand: function() {
      return animal.stand();
    }
  });
}

var dog = dogConstructor(animalConstructor());
var gorilla = gorillaConstructor(animalConstructor());

There's no more hideous messing around with the .prototype property. We have a constructor function for each object type which accepts the dependencies as arguments. Initializing the dependencies directly inside the constructors may also be preferable, but in either case, the implementation is much more pleasant to reason about. It's much easier to write this code ourselves without the help of any external libraries.

What about memory usage?

You may have noticed that this pattern requires a new instance of a common class/module for each object type. Each dog and gorilla object will have a new animal object just for them, which definitely uses more memory than traditional inheritance with shared parent objects. It may be a big deal 10-20 years ago, but not anymore. Memory has become so much more abundant nowadays that saving object allocation hardly ever matters. Just look at the estimated memory usage for the above example in both Java and Javascript below:

Number of Dog objects: 10,000
Number of Gorilla objects: 10,000
Java memory usage with inheritance: ~600kb
Java memory usage with composition: ~1.2mb
Javascript memory usage with inheritance: ~700kb
Javascript memory usage with composition: ~1mb

In today's hardware, that's just peanut. Unless there's memory leak some where in the application, the garbage collector should be able to handle those objects efficiently. Composition is actually better for CPU usage as the interpreter doesn't need to perform look up in the inheritance chain. But again, when writing applications in today's hardware, those micro optimizations don't really matter.

Conclusion

I have stopped using inheritance when writing new code. The only reason inheritance still exists in my life now is because of legacy code written by other people. I'm hoping that by writing this, I can convince some of use to at least give this idea a try. I feel that software engineering and Object-oriented programming in particular needs to evolve to a new era, and we can't do that when inheritance is still around.

Comments

comments powered by Disqus