I'm the head of engineering at Tegus, a financial research startup. I write about politics, psychology, and software.
I've worked on a few open-source projects and infrequently blog.
Recent blog posts:
• Predicting the Success of Pair Programming Interviews
• The Computer Boys Take Over by Nathan Ensmenger
• Who by Geoff Smart and Randy Street
19 March 2016
Inheritance as implemented in most object-oriented programming languages (Ruby, Java, Python, etc) is terrible. We need to stop using it.
Imagine you’re implementing software for an online store that sells books and movies. You’re adding functionality to allow users to add these items to their shopping cart. In particular, you’re writing the function that increases the user’s total purchase by the price of the item. What do you do?
The inheritance-oriented answer is something like this:
class Item
def add_to_cart(cart)
cart.increase_total(this.price)
class Book extends Item
this.price = $10
class Movie extends Item
this.price = $15
On the surface, this looks reasonable. The code to add items to the cart is shared between Book
and Movie
and nothing looks super complicated. You set your keyboard down and go to lunch.
Later, however, the business needs to start tracking the royalty paid to the author of a book separately from the rest of the price. For reasons internal to the book class, you really want to do something like:
class Book extends Item
this.royalty = $2
this.markup = $8
This obviously breaks the contact Book
has with Item
, though, so you need to revise your code. You end up with something like this:
class Item
def add_to_cart(cart)
cart.increase_total(get_price)
def get_price
raise NotImplementedError
class Book extends Item
this.royalty = $2
this.markup = $8
def get_price
this.royalty + this.markup
class Movie extends Item
this.price = $15
def get_price
this.price
This works, and it protects Item
from further changes to the way the price of a Book
is calculated. But it kinda sucks.
First of all, notice how a change totally internal to the Book
class forced us to change the way Movie
is implemented. This happened because the superclass, Item
, relied on a particular internal structure in its child classes. We’ve fixed that problem now, but it’s way more code.
Secondly, Item
now operates at multiple levels of abstraction. It specifies the public interface for adding an item to a cart, but also defines the private responsibilities that something must fulfill in order to be added. Why are these descriptions in the same place? Moreover, it’s unclear if get_price
is intended for public consumption or if it’s supposed to be hidden. In Java we might mark it as protected
, but ideally the intended visibility would be obvious without the need for a modifier.
The better design is to separate the description of how to add an item to a cart from the description of what it means to have a price:
interface Priceable
get_price
def add_to_cart(cart, priceable)
cart.increase_total(priceable.get_price)
class Book implements Priceable
this.royalty = $2
this.markup = $8
def get_price
this.royalty + this.markup
class Movie implements Priceable
this.price = $15
def get_price
this.price
In this design, everything operates at a consistent level of abstraction. The visibility of methods is obvious from the structure of the program rather than requiring declarations. The amount of work required to get a new class to work with add_to_cart
is minimal.
For a long time I was the token Go apologist at my job. I’ve since mellowed in my enthusiasm for Go, but one thing it absolutely gets right is the lack of emphasis on inheritance in favor of interfaces. Rust has done the same thing. I hope this trend continues; I’m disappointed to see that Scala has largely preserved Java’s inheritance model.
A few people have pointed out that Scala has traits, which do exactly what I want. This is true and awesome! However, Scala still has an extends
keyword which functions more or less the same as in Java. I’d have preferred to see it removed.
This post has generated a lot of discussion on Hacker News if you’re interested in reading more.
comments powered by Disqus