Tom Butler's programming blog

Inheritance vs Composition: Composition wins by knockout

Both people who've read my book and anyone who I've taught at university will know that I cover Object-Oriented Programming in detail but never mention inheritance once and never give examples of it. There's a reason for that.

As part of my PhD research I've been collecting programmer's attitudes towards inheritance (among other programming practices) and I've yet to come across an example where it's the better option.

Often developers will say things like:

we recommend preferring composition vs inheritance, and only using inheritance when the using composition would be impractical.

Or the oft-repeated "favour composition over inheritance". This is a cop out. I've yet to find a single case where inheritance offers any distinct advantage over composition. If you need polymorphic behaviour, use interfaces and for relationships composition is always easier to implement and far more flexible.

Here's a phrase I'd prefer to see over "favour composition over inheritance": Any is-a relationship can be expressed using has-a. As it gets the point across more strongly.

James Gosling, the creator of Java once said:

You should avoid implementation inheritance whenever possible.

And I've yet to be shown a case where it's not possible, at least when defining relationships between your own classes. If it's always possible to avoid inheritance we can infer that You should avoid implementation inheritance is the point he is making here, and I agree.

In this article, I'll show you how you can model any is-a inheritance relationship using has-a composition.

Is avoiding inheritance impossible?

When is avoiding inheritance impossible? I can think of a single case: When you do not own the base type but need to override its behaviour while retaining polymorphism. For example, logging all queries sent to the PDO class in PHP:

class LoggingPDO extends PDO {
    public function 
query($sql) {
        
$this->logger->log($sql);
        return 
parent::query($sql);
    }
}

Even then, the adapter pattern using composition may be preferable. Other than that, if you are dealing entirely with classes you own, there is no case when using composition is impossible and I'll show you why.

I'm not going to differentiate between composition and aggregation here, simply because it doesn't matter for these examples.

Employee extends Person

Let's take a few classic examples. Firstly Employee extends Person:

class Person {
    private 
$name;
    private 
$dateOfBirth;
}

class 
Employee extends Person {
    private 
$employeeNumber;
    private 
$jobTitle;
    private 
$salary;
}

I've left out the methods and just concentrated on the properties for this example.

This is a classic is-a relationship starting with a generic Person class and increasing specificity in the Employee class. An employee is-a person.

Person has-a job

There is no reason this needs to be modeled using inheritance. Instead of thinking of an employee as a person you can model it as a person who has a job

class Employee {
    private 
$worker;
    private 
$job;

    public function 
__construct($worker$job) {
        
$this->worker $worker;
        
$this->job $job;
    }
}

$employee = new Employee(new Person, new Job);

I've modeled the same relationship but in a much more flexible manner. Using inheritance, only people can ever be employees. By using composition, employees can be other types:

$guideDog = new Employee(new Dog, new Job);
$marsRover = new Employee(new Robot, new Job);

Using inheritance because Employee extends Person there is no way to have an employee who is a robot or a dog. Perhaps not an issue when the system is built, but may cause trouble in the future.

Any is-a relationship can be flipped around like this and converted into a has-a relationship. A Tesla is-a Car and a Ferrari is-a Car can be flipped around based on the difference that the two variants have. A Tesla has-a ElectricEngine and a Ferrari has-a PetrolEngine

Let's try a more extensive example with some methods.

Cat extends Animal

Here's another classic inheritance example, and using this example I'm going to show why inheritance sucks at even the most simple and contrived tasks:

abstract class Animal {
    private 
$fed false;

    public function 
speak(): string;
    public function 
eat(Food $food): bool;
    public function 
getNumLegs(): int;
}
class Cat extends Animal {
    public function 
speak(): string {
        return 
'meow';
    }

    
// Feed the cat, if the cat cannot eat the type of food it's given, $fed should remain false
    
public function eat(Food $food): bool {
        if (
$food->isMeat()) {
            
$this->fed true;
            return 
true;
        }
        else {
            return 
false;
        }
    }

    public function 
getNumLegs(): int {
        return 
4;
    }
}
class Dog extends Animal {
    public function 
speak(): string {
        return 
'woof';
    }

    
// Feed the dog, dogs can eat anything but chocolate
    
public function eat(Food $food): bool {
        if (
Food instanceof Chocolate) {
            return 
false;
        }
        else {
            
$this->fed true;
            return 
true;
        }
    }

    public function 
getNumLegs(): int {
        return 
4;
    }
}
class Cow extends Animal {
    public function 
speak(): string {
        return 
'moo';
    }

    
// Feed the cow, cows cannot eat meat
    
public function eat(Food $food): bool {
        if (
$food->isMeat()) {
            return 
false;
        }
        else {
            
$this->fed true;
            return 
true;
        }
    }

    public function 
getNumLegs(): int {
        return 
4;
    }
}

This looks sensible and follows a textbook intro to OOP example. We have a generic animal class and provide behaviour for specific animals by extending the class.

But why is inheritance used here? There is no reason this needs to be modeled using inheritance. Ignoring the eat method for a moment, the number of legs and speak string are simple properties. The cow, dog and cat could all be modeled using a single class without inheritance or composition:

class Animal {
    private 
$speak;
    private 
$numLegs;

    public function 
__construct(string $speakint $numLegs) {
        
$this->speak $speak;
        
$this->numLegs $numLegs;
    }

    public function 
speak(): string {
        return 
$this->speak;
    }

    public function 
getNumLegs(): int {
        return 
$this->numLegs;
    }
}
$cat = new Animal('meow'4);
$cow = new Animal('moo'4);
$dog = new Animal('woof'4);

This is less code as only one class needs to be defined and can be more easily extended with new animals. I can create new animals by creating an instance, without needing to write a whole new class.

So what about the eat method that contains some actual behaviour, rather than just a property?

That's slightly harder to model, but it is still easier with composition than inheritance:

interface Diet {
    public function 
canEat(Food $food): bool;
}
class CarnivoreDiet implements Diet {
    public function 
canEat(Food $food): bool {
        return 
$food->isMeat();
    }
}
class HerbivoreDiet implements Diet {
    public function 
canEat(Food $food): bool {
        return !
$food->isMeat();
    }
}
class OmnivoreDiet implements Diet {
    public function 
canEat(Food $food): bool {
        return 
true;
    }
}

The type of diet is separated out from determining whether the animal is fed. The Animal class can then require a Diet instance via composition to allow modeling of animals with different diets:

class Animal {
    private 
$speak;
    private 
$numLegs;
    private 
$diet;
    private 
$fed false;

    public function 
__construct(string $speakint $numLegsDiet $diet) {
        
$this->speak $speak;
        
$this->numLegs $numLegs;
        
$this->diet $diet;
    }

    public function 
speak(): string {
        return 
$this->speak;
    }

    public function 
getNumLegs(): int {
        return 
$this->numLegs;
    }

    public function 
eat(Food $food): bool {
        if (
$this->diet->canEat($food)) {
            
$this->fed true;
            return 
true;
        }
        else {
            return 
false;
        }

    }
}

I've created the exact same class behaviour without needing any subclasses of Animal, yet the different animal types can still be modeled in the application:

$cat = new Animal('meow'4, new CarnivoreDiet());
$cow = new Animal('moo'4, new HerbivoreDiet()));

When unique behaviour is required, for example, a dog being able to eat anything but chocolate, the system can very easily be extended:

class DogDiet implements Diet {
    public function 
canEat(Food $food): bool {
        if (
$food instanceof Chocolate) {
            return 
false;
        }
        else {
            return 
true;
        }
    }
}
$dog = new Animal('woof'4, new DogDiet());

Not only is inheritance not required here, but using composition requires less code and is considerably more flexible.

Inheritance sucks

The problems with inheritance very quickly become apparent. Imagine I wanted to extend the system to model a pig and a human:

class Pig extends Animal {
    public function 
speak(): string {
        return 
'oink';
    }

    public function 
eat(Food $food): bool {
        
$this->fed true;
        return 
true;
    }

    public function 
getNumLegs(): int {
        return 
4;
    }
}
class Human extends Animal {
    public function 
speak(): string {
        return 
'Hello';
    }

    public function 
eat(Food $food): bool {
        
$this->fed true;
        return 
true;
    }

    public function 
getNumLegs(): int {
        return 
2;
    }
}

The obvious problem here is that the eat method for both Pig and Human classes are identical. The fix as we're using inheritance is an extra level of inheritance:

class Omnivore extends Animal {
    public function 
eat(Food $food): bool {
        
$this->fed true;
        return 
true;
    }
}
class Pig extends Omnivore {
    public function 
speak(): string {
        return 
'oink';
    }
    public function 
getNumLegs(): int {
        return 
4;
    }
}
class Human extends Omnivore {
    public function 
speak(): string {
        return 
'Hello';
    }

    public function 
getNumLegs(): int {
        return 
2;
    }
}

This works, but not all humans are omnivores, some do not eat meat. How can we model vegetarian humans in this system? The solution using inheritance is: More inheritance!

class VegetarianHuman extends Human {

    
//Override parent method
    
public function eat(Food $food): bool {
        if (
$food->isMeat()) {
            return 
false;
        }
        else {
            
$this->fed true;
            return 
true;
        }
    }
}

But now we've introduced even more duplicated code: the eat method for VegetarianHuman and Cow are the same and there's no way to fix it without duplicated code somewhere.

Using inheritance, there is no way to model, VegetarianHuman and Human without repeating code somewhere.

Either VegetarianHuman needs extend Human and override the eat method as above or VegetarianHuman needs to extend Herbivore and duplicate the methods in the Human class:

class VegetarianHuman extends Herbivore {
    
// These two methods are identical to the methods in the Human class
    
public function speak(): string {
        return 
'Hello';
    }

    public function 
getNumLegs(): int {
        return 
2;
    }
}

Inheritance always ends up with these kinds of difficulties because it creates a rigid tightly coupled structures and you have to be incredibly careful how you design your classes in order to avoid repeated code. And as I've just shown, it's not always possible even in fairly trivial examples.

Consider the same without using inheritance:

$pig = new Animal('oink'4,  new OmnivoreDiet());
$cow = new Animal('moo'4, new HerbivoreDiet());

$human = new Animal('hello'2, new OmnivoreDiet());
$vegetarianHuman = new Animal('hello'2, new HerbivoreDiet());

By avoiding inheritance I can give any kind of animal any kind of diet because it's not reliant on rigid hierarchies. To create an animal with a special kind of diet, I can create a new type of Diet and inject it into an Animal instance.

You don't need inheritance. Ever. Really.

If you are coming from a background of using inheritance it can be difficult to change your frame of mind from is-a to has-a but once you start getting the hang of thinking in terms of objects with properties rather than types with subtypes it becomes incredibly easy.

To change your way of thinking, assume you create an instance of the base class and assign behaviors to it. Rather than a Manager that is-a Employee consider a Manager a Person that has-a ManagerialJob.

Along with the practical issue of repeated code I showed here, inheritance breaks encapsulation and introduces tight coupling.

There's no reason to use inheritance over composition when defining relationships. Ever. every is-a relationship can be expressed as a has-a relationship. I haven't used, or needed to consider using, inheritance in any of my projects in over 5 years. Take a look at Transphporm, Maphper or XMarkDown none of these projects use inheritance anywhere. It's simply not required and does more harm than good and not something I ever even think about until I encounter it in someone else's code.

As I said at the start, unless you need to override a method in a base class that you didn't write, and the adapter pattern cannot be used, you don't need inheritance, ever. In this case you're not defining a relationship, but overriding some behaviour in a class someone else wrote. And you only need to do that because the original class wasn't designed with this flexibility in mind.