The Liskov Substitution principle  (LSP) states that subclasses must behave the same as their base class.

This is the third of the 5 principles in the acronym S.O.L.I.D, the Liskov’s Substitution Principle, that has the acronym LSP. This principle was introduced by Barbara Liskov.

A definition from the Wikipedia about this principle says:

If S is a subtype of T, then objects of type T may be replaced with objects of type S (in other words, objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, a task performed, and so on).

Robert Martin states that subtypes must be substitutable for their base types, meaning that the behaviour of a subtype must follow the expected behaviour of a base type.

Before we start refactoring, Lets us see some code that violates it. This example is taken from the refund module of an e-commerce application.

The figure shows the classes involved in this example.

lsp1

The RefundService class coordinates the refunding of a RefundRequest by first obtaining the correct payment class via the PaymentServiceFactory. After a refund has been made, the status of the transaction is wrapped within the RefundResponse object and returned to the client.

PaymentType enumeration contains two options PayPal and WorldPay as shown in below code.

The RefundReuest is sent to the RefundService as the single Refund method parameter, and RefundResponse is returned by the RefundService with the status of the refund transaction.

lsp2
MockWorldPayWebService and MockPayPalWebService are simply mock classes that demonstrate the functionality that the payment merchant offer.

lsp4

For the RefundService to interact with the payment merchants as if they had the same interface, we have added PaymentServiceBase class that WorldPayPayment and PayPalPayment can inherit from and wrap the real web service API’s by using Adaptor pattern.

The RefundService interact with the abstract PaymentServiceBase class and is unaware of which real implementation it is dealing with.

lsp3

Next two classes are the RefundService and the PaymentServiceFactory, which is responsible for creating the concrete implementation of the payment adapter.

lsp5

The PaymentType enum is passed and the matching concrete payment adapter is created and returned to the caller.

Finally, RefundService class as shown below.

lsp6

Where is the issue?

At first, it might seem that the code is fine. It should be immediately obvious that there is a problem.

  1. It is not possible to substitute the subtype for its base type because each implementation of the payment adapter must be handled differently. The downcasting of the base class is another code smell that breaks the LSP;
  2. Return code section breaks the principle in that required to handle all cases for the subtypes; thus, you cannot substitute the subtype without ensuring you have code specific to that subtype.

lsp7

We can resolve these issues without too much pain.

Let’s tackle the problem of the downcasting. Without the respective merchant’s login credentials, the web service method cannot be called. Both adapters depend on these values, so we need to move these parameters into the constructor so that neither adapter can be created without them.

Let us refactor the code as shown in the figure below.

lsp8

The login details are a hardedcoded string to keep things simple. In a real application, you can store these credentials in some kind of configuration file.

Now we will see the second issue with the RefundService class related to the refund transaction response.  Currently, the RefundService class has to inspect the result of the transaction and ensure that it matches the authorization criteria of one the subtypes, which again break the  LSP. So to address this by changing the  return type from string to the RefundResponse object as shown in the refactored code below.

lsp9

lsp10

In this way, we can create a relationship between the subclass and the base class by adhering to the Liskov Substitution principle. Common ways to identify violations of LS principles are as follows:

  1. Not implemented the method in the subclass.
  2. Subclass function overrides the base class method to give it new meaning.

 

Please feel free to comment your opinion about this article or whatever you feel like telling me. Also if you like this article, don’t forget to share this article with your friends. Thanks!

Happy Coding !!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.