DEV Community

Muhammad Salem
Muhammad Salem

Posted on

Understand the Liskov substitution principle using a simple example.

Imagine you're building a delivery system.
Consider an entity class Vehicle and its subclass Car.

public class Vehicle
{
    public virtual void StartEngine()
    {
        // General engine start logic
    }

    public virtual void LoadCargo(int weight)
    {
        // General cargo loading logic
    }
}

public class Car : Vehicle
{
    public override void StartEngine()
    {
        // Car-specific engine start logic
    }

    public override void LoadCargo(int weight)
    {
        if (weight > 500)
        {
            throw new InvalidOperationException("Cars cannot carry more than 500kg");
        }
        // Car-specific cargo loading logic
    }
}
Enter fullscreen mode Exit fullscreen mode

The code violates the Liskov Substitution Principle (LSP) because of the LoadCargo method in the Car class. Here's why:

  1. Base Class Contract: The base class Vehicle defines a LoadCargo(int weight) method with seemingly generic cargo loading logic. This implies any Vehicle can handle loading cargo of any weight.

  2. Derived Class Restriction: The derived class Car overrides LoadCargo and introduces a weight limit of 500kg. This contradicts the base class contract by throwing an exception for weights exceeding the limit.

  3. Substitutability Issue: If you use a Car object anywhere that expects a Vehicle for cargo loading, you might encounter unexpected behavior (the exception) when the weight exceeds 500kg. This breaks the principle of seamless substitution.

How to Fix the Violation:

Solution : Refine Base Class Contract (Weight Limit Property):

public class Vehicle
{
    public virtual int MaxWeightLimit { get { return int.MaxValue; } } // Default unlimited weight

    public virtual void StartEngine()
    {
        // General engine start logic
    }

    public virtual void LoadCargo(int weight)
    {
        if (weight > MaxWeightLimit)
        {
            throw new InvalidOperationException("Vehicle cannot carry more than its weight limit");
        }
        // General cargo loading logic
    }
}

public class Car : Vehicle
{
    public override int MaxWeightLimit { get { return 500; } }

    public override void StartEngine()
    {
        // Car-specific engine start logic
    }

    public override void LoadCargo(int weight)
    {
        // Car-specific cargo loading logic (can be empty if no specific logic needed)
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • We introduce a MaxWeightLimit property in the base class Vehicle with a default value of int.MaxValue (effectively unlimited).
  • The base class LoadCargo method now checks against MaxWeightLimit.
  • The Car class overrides MaxWeightLimit to set its specific limit of 500kg.
  • This way, both Vehicle and Car consistently handle weight limits through the base class contract, adhering to LSP.

Top comments (0)