Composition Over Inheritance

Inheritance isn’t all bad, but in some instances it doesn’t make sense to use it.


What is inheritance?

Inheritance is known as a IS-A relationship, i.e a dog IS A animal, a taxi IS A vehicle.

With inheritance you can avoid code duplication by inheriting behaviours - it’s powerful but it can be reused and can lead to designs which are too rigid.


class Dog extends Animal {}
class Animal {}

$dog = new Dog();

class Taxi extends Vehicle {}
class Vehicle {}

$taxi = new Taxi();

What is composition?

Composition is known as an HAS-A relationship, i.e a dog HAS An owner, a taxi HAS A passenger.


class Dog {}
class Owner {}

$dog = new Dog(new Owner());

class Taxi {}
class Passenger {}

$taxi = new Taxi(new Passenger());

But why favour this over inheritance?

Coffee shop problem

Consider a shop which sells coffee, they have a simple class which represents their coffee:


class Coffee {
    public function prepare() {}
    public function cost() {}
}

We start using inheritance to represent different types of coffee, by extending the base class (Coffee) and overriding the prepare and cost methods:


class CoffeeWithMocha extends Coffee {}
class CoffeeWithButter extends Coffee {}
class CoffeeWithMilk extends Coffee {}

But what happens if we need a Coffee with Milk and Butter? Or there’s new toppings? What if the cost of Milk changes, we’re going to have to make several changes across the classes where a Coffee has Milk.

Potentially the code here isn’t extensible or maintainable (in the long term).

How about redefining the relationships? Instead of CoffeeWithButter IS-A Coffee, why not consider Coffee HAS-A Condiment? (Lets favour composition over inheritance in this example).

The solution

Inheritance isn’t all bad, but in some instances it doesn’t make sense to use it. We could use inheritance to set up a group of Condiments.


class Condiment {
    public function prepare() {}
    public function cost() {}
}

class Mocha extends Condiment {}
class Butter extends Condiment {}
class Milk extends Condiment {}
class Caramel extends Condiment {}

We could add attach the Condiments to the Coffee class.


class Coffee {

    public $condiments = [];

    public function prepare() {}
    public function cost() {}

    public function addCondiment(Condiment $condiment) {
       $this->condiments[] = $condiment;
    }
}

$coffee = New Coffee();
$coffee->addCondiment(New Butter());
$coffee->addCondiment(New Milk());

Whats changed?
  • We can add any number of condiments easily at runtime.
  • There’s no code duplication issues (i.e what happens we if now need to change the price of Milk?)
  • Avoided a class explosion we were seeing each time we needed a new Coffee variant.