SOLID Design Principles - Liskov Substitution Principle

2020, November 22

SOLID is a set of five design principles used in object-oriented programming to make software easier to understand, flexible and maintain.

Robert C. Martin introduced the theory of SOLID in year 2000 on his paper Design Principles and Design Patterns. Later, Micheal Feathers introduced the SOLID acronym.

SOLID stands for

  • Single-responsibility Principle
  • Open-closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

Following writing will discussed on one of the principles which is Liskov Substitution Principle (LSP).


Table of contents


Concept

Let Φ(x) be a property provable about objects x of type T. Then Φ(y)should be true for objects y of type S where S is a subtype of T.
-Barbara Liskov and Jeannette Wing

The general idea of LSP is substitutability, where a superclass can be replaced with its subclass without altering any properties of the application. Thus, objects of the subclasses required to behave in similar to the objects of the superclass.

In object-oriented programming (OOP) such as Java has a concept called Inheritance. Inheritance is a concept where a class is based on another class. Where a class is "inherited" from another class, the inherited class become the subclass of the superclass, thus has all the characteristics of the superclass, but can also contain new properties.


Example

LSP is easier to abuse due to this principle is depends on the behavior of the classes than structures. Thus, the compiler would not catch any error in the application and it will work, but mostly lead to buggy or difficult to maintain code.

Bad Example

Below is the bad application of LSP for vehicle application.

public class Vehicle {

  private String name;
  private double speed;

  // public setter and getter method

  public void startEngine() { ... }
}

Vehicle.java

public class Car extends Vehicle {

  @Override
  public void startEngine() { ... }
}

Car.java

Above shown Car class is a subclass of Vehicle class. Thus, Car is overriding the startEngine method of its superclass.

public class Bicycle extends Vehicle {

  @Override
  public void startEngine() { ... }
}

Bicycle.java

But if Bicycle class inherited Vehicle class, it leads to incorrect behavior due to bicycle do not have any engine. Therefore, the startEngine method is redundant and useless for Bicycle class.

Therefore, this violate the LSP which result in a method in class does nothing, or simply cannot be implemented due incorrect behavior.

Good Example

To fix the violation, each vehicle need to classify either with or without engines. In this case, bicycle as vehicle without engine while car with engine.

public class Vehicle {

  private String name;
  private double speed;

  // public setter and getter
}

Vehicle.java

public class VehicleWithEngine extends Vehicle {

  public void startEngine() { ... }
}

VehicleWithEngine.java

public class VehicleWithoutEngine extends Vehicle {

  public void startMoving() { ... }
}

VehicleWithoutEngine.java

public class Car extends VehicleWithEngine {

  @Override
  public void startEngine() { .. }
}

Car.java

public class Bicycle extends VehicleWithoutEngine {

  @Override
  public void startMoving() { ... }
}

Bicycle.java

Therefore, with more classes to differentiate vehicle with or without engine, the implementation now adhere to LSP.


Conclusion

In conclusion, Liskov Substitution Principle enables developer to replace a parent class with subclass without breaking the application.

While to apply the principle can be achieved by using inheritance in OOP, inheritance can easily misuse which lead to buggy or difficult to maintain code although the application is running fine.

Thus, developer need careful about the behavior of class when developing an application to avoid violate the LSP.


References