More Object-oriented JavaScript

I want to review the points in my last article about object-oriented JavaScript, hopefully in a less rambling fashion. My big insight is that inheritance is inheritance. More specifically, class-based inheritance and prototypal inheritance are different mechanisms for accomplishing the same thing, namely single inheritance. Both mechanisms have the same strengths and limitations.

Inheritance Means “is a”

Inheritance—and single inheritance in particular—indicates an “is a” relationship between the child and the parents. Classic examples of inheritance in Java textbooks and introductory programming guides will have a parent class called Animal and child classes with names like Dog and Cat and Elephant. These relationships are fine. A dog is an animal and a cat is an animal, so it makes sense for the Dog and Cat classes to inherit from the Animal class.

The other common example is to have a parent class called Shape and child classes called Triangle and Square. Again, these relationships are fine. Triangles and squares are definitely shapes, so this class hierarchy passes our test.

Now let’s look at some head-scratchers. These come from Joshua Bloch, former Chief Java Architect at Google, in his book Effective Java. In Java, the Properties class represents a persistent set of properties, but it extends the Hashtable collection class. Does this make sense? Let’s try our sentence. Properties is a hashtable. Um, no. No, I don’t think that makes a damn lick of sense.

Similarly, the Stack class extends Vector. Stack is a vector? Well, that makes a little more sense, but that’s really an implementation detail and not an inheritance relationship. In other words, I can think of a stack that’s not a vector, but I can’t think of a cat that’s not an animal.

Correct Inheritance is Hard

Bloch writes:

Inheritance is a powerful mechanism for code reuse, but it is problematic because it violates encapsulation. It is appropriate only when a genuine subtype relationship exists between the subclass and the superclass. Even then, inheritance may lead to fragility if the subclass is in a different package from the superclass and the superclass is not designed for inheritance. To avoid this fragility, use composition and forwarding instead of inheritance, especially if an appropriate interface to implement a wrapper class exists. Not only are wrapper classes more robust than subclasses, they are also more powerful.

The take-home message is this: Only use inheritance when there is a true “is a” relationship between the child and the parent and when you control the code of both the parent and the child. Otherwise, avoid it. Inheritance should be “off” by default.

This applies equally to class-based inheritance in Java and prototypal inheritance in JavaScript. Deep class hierarchies are bad, and long prototype chains are bad. They are fragile—they break easily—and there are too many places for a touch of carelessness to send the whole thing off the rails.

Favor Composition over Inheritance1

Composition is good. In Java, they have to use interfaces and forwarding classes to achieve composition. It’s pretty straightforward once you get your head around it, but it’s not a pattern that they teach in school.

Like many things, it’s much simpler to use composition in JavaScript. We just use our old (yet powerful) friend, the “mixin” pattern.2 We’ll take a deep dive into composition in both Java and JavaScript in a future post in this series.

  1. I’m stealing from Bloch again, as this is the title of the relevant section of his book. I stand on the shoulders of giants. [return]
  2. I find the name “mixin” somewhat annoying. Is it a noun? Is it a verb? It doesn’t really mean anything. I recently read that this pattern is called “traits” in different languages (Scala, perhaps; I’ve forgotten the source). I think this is a much better name because it explains the whole pattern in a singular word. I wonder if I can get this to catch on the in JavaScript community. [return]