SOLID – The Liskov Substitution Principle

In the previous post I wrote about the O in SOLID, The Open/Closed Principle. Now it is time for the L, The Liskov Substitution Principle (LSP).

Definition

In the year 1988, the American Computer Scientist Barbara Liskov, wrote

What is wanted here is something like the following substitution property: If for each 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 substituted for o2 then S is a subtype of T.

In other words this means that subtypes must be substitutable for their base types. An example of a violation of LSP would be: Given a method M(B) where B is an object of type BaseClass. If M(B) behaves badly given an object D of type DerivedFromBaseClass then D violates LSP.

Code Smells

Violation of LSP leads to Fragility. Parts of your code might break in subtle ways when adding new derived classes.

Attempts to fix this issues might lead to violations of OCP, which in turn introduces Rigidity and possibly also Immobility.

How to apply

Unlike the two previous principles, SRP and OCP, there are no named patterns to apply in order to fix violations of LSP. Manual inspection and unit testing comes a far way when it comes to detect violations and then you will need to determine from case to case how to fix the violation.

It can be tempting to put a test in the code to determine the type of the object passed to the method:

public void Method(Base b)
{
  // Do NOT do this. It violates OCP
  if (b is Derived d) { ... }
  else { ... }
}

These types of tests however violates OCP since it is no longer possible to add new derived types without changing existing code.

What can be done is, instead of adding type tests, you separate the violating methods from the existing interface. This will leave you with a common interface, that all classes can implement, and a specialized interface that contains the methods that one or more classes should not implement. For example, let’s assume that we have an interface, IF, that is implemented by the classes CA, CB, and CC. Now, assume CC conforms to LSP for methods MA, MB, and MC but violates LSP for methods MD and ME. You can then break out MA, MB and MC from IF and move them to a new interface IFCommon. You then let IF derive from IFCommon, let CA and CB still implement IF but let CC implement only IFCommon and it’s own versions of MD and ME. See the diagrams below:

CC violates LSP since it’s implementations of MD and ME behaves badly
The code now conforms to LSP. It is no longer possible to use CC where IF is expected.

Summary

The Liskov Substitution Principle puts tougher demands on derived classes than just the fact that they have a common base class. I demands that it should be possible to use the derived classes instead of the base classes, without breaking anything. Conformance to LSP is an enabler for OCP, since for it to be possible to extend the current behavior, by adding new derived classes, these must work as expected when used as their base class.

We are now more than half way through SOLID, next up is The Interface Segregation Principle.

Lämna en komentar

E-postadressen publiceras inte. Obligatoriska fält är märkta *