So far in this series on SOLID software development, we have considered the Single Responsibility Principle and the Open/Closed Principle. That brings us to the one with the strange name: the Liskov Substitution Principle.
We have already seen an example. We had a class, DocumentSearcher and were able to substitute a class that added logging behavior (LoggingSearcher) because it had the same interface as the non-logging version.
That's all the Liskov Substitution Principle is: the abilty to swap one class for another that shares its interface, with no surprises. Or, more exactly, you can use a derived class in place of its base class "without altering any of the desirable properites of the program."
It's a simple idea, but don't take it casually. Violations are surprisingly easy to create. For example:
- Your base class, MessageBroadcaster, happily does nothing if the list of recipients is empty, but your derived class, EmailBroadcaster, throws an ArgumentException.
- Your SimpleTextParser class has a SeparatorCharacters property that your derived RegularExpressionParser ignores.
- Worse, RegularExpressionParser repurposes SeparatorCharacters to hold the regular expression, with the result that it cannot tolerate a SeparatorCharacters string that is not a regular expression, so the caller must know which class it's really using.
- To take advantage of LINQ's wonderful capabilities, you make your collection class implement IQueryable, but fail to implement it completely.
- The classic (and subtle) example centers on why a Square class should not inherit from a Rectangle class, in spite of the fact that a square "is a" rectangle. You can read the particulars here, but it comes down to the fact that consumers of a Rectangle class will expect it to have independent Height and Width properties, but a derived Square class must violate that expectation.
Next time, we'll continue with the Interface Segregation Principle.