Favor Composition Over Inheritance part 1

“Favor composition over inheritance” is a phrase that I hear spoken a lot but which describes a concept I rarely actually see in real world code.  Every developer seems to know about it but few developers seem to actually put it into practice. This post will be looking at inheritance and some of the pitfalls of trying to create your domain model primarily through inheritance.

I can only assume that the reason why inheritance is so overused in real world code is due to the way that it is taught.  Back, far too many years ago, while I was still studying at university, the concepts of inheritance and polymorphism where both taught side by side, very early in the object oriented programming course.  It seems as though these lessons were particularly memorable, because so much real-world code has giant inheritance chains.  We have ObscuredItems inheriting from DataItems inheriting from BasicItems which inherit from Items which inherit from BaseObjects.  Often times you will have to go five or six classes deep to find the root cause of a bug.

Favoring composition over inheritance helps us flatten those structures.  To illustrate this, I am going to take a look at a very simple problem.

I want to model real-world cars.  Specifically, I initially want to model the changes that a driver is applying to each car.  The customer in this case only wants me to model a Toyota Corolla.  The class should also have a string for the manufacturer name.  In this case I will deliberately favor inheritance.

Firstly, I’ll set up an abstract BaseCar class where I will define what I need: 4 wheels, a method for acceleration, a method for turning left and a method for turning right.

abstract class BaseCar
{
  public Wheel FrontLeft { get; protected set; }
  public Wheel FrontRight { get; protected set; }
  public Wheel RearLeft { get; protected set; }
  public Wheel RearRight { get; protected set; }
  public abstract string Manufacturer { get; }

  public abstract void TurnLeft();
  public abstract void TurnRight();
  public abstract void Accelerate(double kmsPerHour);
}


Next up, I will implement the functionality in my concrete car class.

abstract class Car : BaseCar
{
  protected Car()
  {
    this.FrontLeft = new Wheel();
    this.FrontRight = new Wheel();
    this.BackLeft = new Wheel();
    this.BackRight = new Wheel();
  }

  public override void TurnLeft(double degrees)
  {
    this.FrontLeft.TurnLeft(degrees);
    this.FrontRight.TurnLeft(degrees);
  }

  public override void TurnRight(double degrees)
  {
    this.FrontRight.TurnRight(degrees);
    this.FrontLeft.TurnRight(degrees);
  }

  public override void Accelerate(double kph)
  {
    this.FrontLeft.Rotate(kph);
    this.FrontRight.Rotate(kph);
  }
}


Finally, I will then implement a concrete ToyotaCorolla class.

class ToyotaCorolla : Car
{
  public override string Manufacturer
  {
    get { return "Toyota"; }
  }
}

All fairly straight forward.  We have a nice little inheritance chain that seems to make sense (although perhaps I could have done away with the BaseCar class).

I submit this code to the customer and they are very happy.  They’re so happy that they will pay me more money to add extra features.  They now want me to implement a ToyotaCorollaSports class, which contains a rear-wheel drive version of the car.  In all other aspects it’s the same car, it’s only that the rear wheels are now powering the car.

Simple enough, I can inherit from the ToyotaCorolla and override the accelerate method.  I make the change and submit it to the customer.  They like what they see.

class ToyotaCorollaSports : ToyotaCorolla
{
  public override void Accelerate(double kph)
  {
     this.RearLeft.Rotate(kph);
     this.RearRight.Rotate(kph);
  }
}

Two weeks later the customer has come back.  They love what we’ve done and want us to implement HondaCivic and HondaCivicSports classes.  Now we run into our first real problem.  The rear-wheel drive code is actually in the concrete ToyotaCorollaSports class.  What we need to do now is actually refactor our Car class into FrontWheelDriveCar and RearWheelDriveCar.  HondaCivic and ToyotaCorolla can then both inherit from the FrontWheelDriveCar class and the sports editions can both inherit from the RearWheelDriveCar class.

abstract class FrontWheelDriveCar : Car
{
  public override void Accelerate(double kph)
  {
    this.FrontLeft.Rotate(kph);
    this.FrontRight.Rotate(kph);
  }
}

abstract class RearWheelDriveCar : Car
{
  public override void Accelerate(double kph)
  {
    this.RearLeft.Rotate(kph);
    this.RearRight.Rotate(kph);
  }
}


Our customers are very happy with what we have done so far and decide to commission us to create a four wheel drive Mitsubishi Titan.

So we now have to create an AllWheelDriveCar and inherit from that for our Mitsubishi Titan.

abstract class AllWheelDriveCar : Car
{
  public override void Accelerate(double kph)
  {
    this.FrontLeft.Rotate(kph);
    this.FrontRight.Rotate(kph);
    this.RearLeft.Rotate(kph);
    this.RearRight.Rotate(kph);
  }
}

class MitsubishiTitan : AllWheelDriveCar
{
  public override string Manufacturer
  {
    get { return "Mitsubishi"; }
  }
}

class diagram
Finally, the customer comes along and asks us to model for them a new experimental four wheel drive buggy that is designed to go on sand and turns with all four wheels.  A two wheel drive buggy is also available (it also turns with all four wheels).

Now we have a problem.  We will obviously be deriving from our four wheel drive class for the four wheel drive model and the two wheel drive class for the two wheel drive model, but we will also have to override our TurnLeft and TurnRight methods in both classes, and the code will be duplicated.

Arggg!  Duplicated code is the enemy of maintainability. The best solution to this problem is to switch our thinking. Instead of inheriting all of our functionality, we should compose our classes from pieces of related functionality.

Next up: a better way to solve the same problem.

About these ads

3 thoughts on “Favor Composition Over Inheritance part 1

  1. I agree that inheritance is over used and used in places it shouldn’t. However, in terms of duplicating code, I’m not sure that composition will do any better. Composition requires you to duplicate the parts linking the composite parts to the interface. With a sufficient number of concrete classes that fall into categories, you end up duplicating more using composition than you would with inheritance.

    In this case, I’d go with composition, not because of duplication but because it more naturally follows a “has a” relationship rather than an “is a” relationship.

    • This is a rather contrived example, specifically being used to demonstrate how inheritance chains can break. You are right that there is often duplicated code when using composition. No technique is perfect.

      There are a number of cases where inheritance is appropriate and the best thing to use. In the winforms paradigm, inheritance is necessary for a number of things to work properly. The problem is that inheritance tends to be overused while composition tends to be vastly underused. That’s why the principle is “favor composition over inheritance”.

      Thinking in terms of “has a” vs “is a” I think is a great way of teaching inheritance, but it does not map quite so well to large scale applications. The reason is that much of the time, the “is a” relationship mapping between real world objects (a square is a rectangle) can cause Liskov Substitution principle violations. I’ll probably write more about the Liskov Substitution principle later on.

      Now in the example I’ve used here, if I was using C++ I could use multiple inheritance to inherit both four-wheel steering and four-wheel drive, but that’s not an option in a number of languages, including C#. In any case, I’d much prefer to work in a code base which had zero inheritance than much of the real-world code that I have worked on.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s