Principles, SOLID

SOLID – Liskov Substitution Principle (LSP)

As stated previously… I am getting (or trying to get) back to basics.

It is my belief that developers have to often times refactor the way they think and the way they approach things from time to time to make sure that the basics and principles are still strong.

So let’s take a look at one of the principles in SOLID; Liskov Substitution Principle (LSP)

LSP states:

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

Right, to many this is as clear as mud. It took me a while to understand what this principle was really talking about at first. Then after reading a few papers written on the subject it became a little more clear. What I have seen many developers do, however, is paraphrase this principle (or actually rewrite it) to make it easier to understand.

Commonly, LSP is paraphrased to state:

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it

So really all we are talking about is making sure our methods use base classes or interfaces as parameters and that the method does not have to know about all of the derived types of the base class or the classes that implement an interface.

Perhaps it is best to use an example to better explain what this principle is talking about.

public class Customer {
  ...
}

public class Vendor : Customer {
  ...
}

public class CustomerValidator {
  public void Validate(Customer customer) {
    if (string.IsNullOrEmpty(customer.Name)) {
      /// indicate that validation has failed
    }
    var vendor = customer as Vendor;
    if (vendor != null) {
      /// we know we are dealing with a Vendor
      /// validate Vendor rules
    }
}

Here we have a Customer class and a Vendor class which inherits from Customer. In the CustomerValidator class we are correctly using the Customer class (which would allow a Customer or Vendor object to be used… good so far) but then the Validate method has to know about the Vendor type and therefore violates LSP. What we can also (hopefully) see here is that we are violating the Open Closed Principle (OCP) due to the fact that any time we create another derived type from Customer we must modify the Validate method to handle the new type (hence it is closed for extension and is open for modification).

So how do we correct the above to remove the LSP violation?

The following is one way (and probably not the best way) to resolve the issues.

public class Validator {
  private readonly IList<string> errors;

  public Validator() {
    this.errors = new List<string>();
  }

  public void Validate(IValidation validation) {
    validation.Validate(errors);
  }
}

public interface IValidation {
  void Validate(IList<string> errors);
}

public CustomerValidation : IValidation {
  private readonly Customer customer;

  public CustomerValidation(Customer customer) {
    this.customer = customer;
  }

  public virtual void Validate(IList<string> errors) {
    /// if we fail validation add an error to the collection
  }
}

public VendorValidation : CustomerValidation {
  private readonly Vendor vendor;

  public VendorValidation(Vendor vendor)
    : base(vendor) {
    this.vendor = vendor;
  }

  public override void Validate(IList<string> errors) {
    /// call the Customer validation routines
    base.Validate(errors);
    /// if we fail validation add an error to the collection
  }
}

Wow. We have changed things up a lot.

If we look at the Validator class we can see that the Validate method takes a parameter which allows us to pass in any class that implements the IValidation interface. This follows LSP because we can substitute any class which implements this interface without changing the behavior. Not only that but the method doesn’t need to know anything about the types which implement the interface. At the same time, we have also solved our OCP violation by making the CustomerValidation class open for extension and closed for modification.

One more important thing that I would like to point out that sometimes get left out when explaining LSP is that exceptions should (or must) follow the same rules. This means that if you are throwing an exception in a method of a base class then any derived type overriding the method should throw the same type of exception. By doing this you can be ensured that your error handling for the base class will be correct for derived types as well. Just something that you should keep in mind.

Advertisements
Standard

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s