Problems with inheritance

Composite Reuse Pattern

Definition: "Favor delegation over inheritance as a reuse mechanism" ie Composition Over Inheritance

Patterns for composition

I have experienced multiple issues with inheritance as a reuse mechanism. I will layout some simple contrived examples but real world situations are far more complex than these examples making them more prone to these types of issues.

Lets look at the following classes as an example:

public class TieFighter
{
    public int Shoot()
    {
        // some calculation
        return 30;
    }

    public int Fly()
    {
        // some calculation
        return 70;
    }

}

public class XWing
{
    public int Shoot()
    {
        // some calculation
        return 30;
    }

    public int Fly()
    {
        // some calculation
        return 70;
    }
}

If you have the above two class you may be tempted to move the "Shoot" and "Fly" method into a seperate abstract class and have these two classes inherit from it.

public abstract class SpaceShip
{
    public virtual int Shoot()
    {
        // some calculation
        return 30;
    }

    public virtual int Fly()
    {
        // some calculation
        return 70;
    }
}

public class TieFighter : SpaceShip
{
}

public class XWing : SpaceShip
{
}
Problem with unintentional changes and lack of visibility

Inheritance hierarchies can become deep and complex. When functionality is hidden by layers and layers of inheritance the lack of visibility can cause a lack of understanding and all types of bugs to creep up.

If a developer which has never worked on our space ship project gets asked to change the calculation of the X-wing, he may simply find where the calculation is and change it, like this:

public abstract class SpaceShip
{
    public virtual int Shoot()
    {
        // new calculation
        return 40;
    }

    public virtual int Fly()
    {
        // some calculation
        return 70;
    }
}

The problem with this is that the tie fighter has now been unintentionally changed because it also inherits from "SpaceShip". These types of mistakes are all too common and create bugs in our software.

collapse
Problems with handling different combinations

Then you get asked to add another spaceship, the Tie Bomber, but it has different calculations to the defaults inherited... no problem cause you just decide to override it

public class TieBomber : SpaceShip
{
    public override int Shoot()
    {
        // different calculation
        return 90;
    }

    public override int Fly()
    {
        // different calculation
        return 10;
    }
}

Then you get asked to add another spaceship, the Y-wing, which requires the same shoot calculation as the Tie Bomber but a brand new fly calculation... so you override this as well override it


public class YWing : SpaceShip
{
    public override int Shoot()
    {
        // Same as TBomber 
        // Note that this leads to code duplication
        return 90;
    }

    public override int Fly()
    {
        // 3rd type of calculation
        return 10;
    }
}

Now imagine you have to add another 50 classes each sharing different combinations of methods, with inheritance you will either have to override everything which means you can't use inheritance for sharing many aspects and you may have to duplicate code when overriding, or create many different combinations to inherit from (which becomes complex and messy).

collapse

Instead of using inheritance as a reuse mechanism we can use composition for reuse.


public class SpaceShip
{
    private readonly IWeapons _weapons;
    private readonly IEngine _engine;

    public SpaceShip(IWeapons weapons, IEngine engine)
    {
        _weapons = weapons;
        _engine = engine;
    }

    public int Shoot()
    {
        return _weapons.Shoot();
    }

    public int Fly()
    {
        return _engine.Fly();
    }
}

The components which make up a space ship can be created indepently from the space ship its self.

public class LightLasersWeapons : IWeapons
{
    public int Shoot()
    {
        // some calculation
        return 30;
    }
}


public class FastEngines : IEngine
{
    public int Fly()
    {
        // some calculation
        return 70;
    }
}

We can then use the single SpaceShip class to build all our different types of space ships, by recieving different combinations of weapons and engines.


    var tieFighter = new SpaceShip(lightLasersWeapon, fastEngine);
    var tieBomber = new SpaceShip(bombWeapons, slowEngine);
    var tieSilencer = new SpaceShip(heavyLasersWeapon, fastEngine);

When creating new spaceships we will only need to mix and match the components or create new components.

Check out these links for more info:

My design and architecture repo