SOLID Made Easy – Liskov Substitution Principle

The third principle in the SOLID principles is the Liskov Substitution Principle (LSP), it was named after Barbara Liskov as together with Jeannette Wing she was able to come up with a definition for subtyping. They defined the principle as:

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.

This principle simply states that all subclasses should operate the same way as the base class. It is also stated that in order to comply with the LSP we must follow these rules:

  • The parameters in the subclass must be less restrictive or the same as those in the base class.
  • The return types of the subclass must be more restrictive or the same as the return type of the base class.
  • The preconditions of a base class must not be strengthened by the subclass.
  • The postconditions of a base class must not be weakened by the subclass.
  • The invariants of a base class must not be changed by a subclass.
  • The subclass should not change the state of an object that is not permitted by the base class.
  • The subclass cannot throw an exception that is not thrown by the base class unless the exception is a subtype of the exception that may be thrown in the base class.

Here is a sample code that shows how we can violate this principle:

The code would work, but it could have been written in a better way. Here are some of the areas that we can focus on to improve the code:

In the PaymentFactory class, we check for the type of payment first; we are violating the LSP because that shows that the subclass is not substitutable for its base class. Also, when a new payment service needs to be implemented we also need to modify the PaymentService to set the values to the parameters of the new payment service if necessary and to check for the response of the new payment service which violates the Open-Closed Principle.

Here are the changes that we made on the code:

  • By changing the return type of the Process method from the response string to the boolean result in the PaymentBase abstract class, we now force each payment subclass to evaluate the response that will be returned as the result. Eliminating the need to modify the PaymentService whenever a new kind of payment service response needs to be evaluated.
  • By adding a constructor to the PaymentBase subclass i.e. the CreditCardPayment class we can now set the required parameters from the PaymentFactory instead of the PaymentService. With this change the PaymentService class does not need to be modified whenever a new type of payment service needs to be implemented.

With these changes, the PaymentService simply tells the factory to use a subclass of the PaymentBase. The PaymentService no longer needs to check what payment type it is going to process to set the initial parameter values and to evaluate the response. In return, the PaymentService would receive whether the processing of the payment is successful or not.

Practicing the LSP enables us to extend a base class without changing any behavior from the existing classes. It makes our work easier and makes us more efficient as we will write less code that of course, leads to fewer mistakes.