Friday, September 15, 2017

SOLID - 3. The Liskov Substitution Principle

Continuing the series about the five SOLID principles, today I invite you to explore the Liskov Substitution Principle (LSP). 

Remember all FIVE principles, and the meaning of the SOLID acronym:


You can read up on my other articles on SOLID here:


The Liskov Substitution Principle


This principle takes its name Barbara Liskov who first presented it at a conference in 1987.

The most commonly used definition says:

"Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it."

That is a simpler way of explaining the formal definition of Liskov:

"If for every object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is replaced by o2 then S is a subtype of T".

A subclass should overwrite the methods of the parent class in such a way that it does not break functionality from the client's point of view.

Lets assume we want to program a race simulation with different kinds of vehicles:

public abstract class Vehicle
{
    public abstract void StartEngine();

    public abstract void SetDriver(string driver);       
}

public class Truck : Vehicle
{
    public override void StartEngine()
    {
        //..
    }

    public override void SetDriver(string driver)
    {
        //..
    }
}

public class Motorbike : Vehicle
{
    public override void StartEngine()
    {
        //..
    }

    public override void SetDriver(string driver)
    {
        //..
    }
}

public class RaceSimulator
{
    private IList<Vehicle> _vehicles;
    private IList<string> _drivers;

    //..

    public void Initialize()
    {
        foreach (string driver in _drivers)
        {
            Vehicle v = GetRandomVehicle();
            v.SetDriver(driver);
            v.StartEngine();                
        }
    }
}

This code looks good and works fine until we introduce a new type of vehicle:

public class Bicycle : Vehicle
{
    public override void StartEngine()
    {
        throw new NotImplementedException();
    }

    public override void SetDriver(string driver)
    {
        //..
    }
}

Here we created a violation of the LSP. A bicycle is also a type of vehicle, but since it has no engine it behaves different to the other vehicles in the example. This is why when you are thinking about inheritance you should not only think about if B is a type of A but more importantly if B behaves like A does.

In addition, this violation may result in a violation of the Open/Closed principle, causing all other consequential problems of it:

public void Initialize()
{
    foreach (string driver in _drivers)
    {
        Vehicle v = GetRandomVehicle();
        v.SetDriver(driver);

        if (!(v is Bicycle))
        {
            v.StartEngine();
        }
    }
}


Conclusion

The LSP is an extension of the Open/Closed Principle. It ensures that new derived classes are extending the base classes without changing their behavior.

When meeting the Liskov Substitution Principle, derived classes are replacable by their base classes and any code that uses the base class will be able to meet the Open/Closed Principle, facilitating high maintainability.