Welcome to part three of the SOLID OOP design pattern series. You may wish to subscribe to the blog to stay tuned in as I post new issues in this series..
SOLID is not a language, it’s not a framework, it’s a set of principles to help guide your team to design better applications. These principles will help ensure your code is adaptive to change. In this series, I’ll be using C# for examples, but the same principles apply to any OOP language: Python, Java, C++, etc.. Using SOLID design principles requires a fundamental understanding of classes, interfaces, and class inheritance.
The third letter in SOLID stands for LSP: Liskov Substitution Principle. This principle, first introduced in 1988 by computer scientists Barbara Liskov and Jeannette Wing, states, “if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program.” Simply stated, base classes must be interchangeable with other derived classes.
Please do yourself a huge favor and watch Barbara Liskov’s presentation here on programming methodologies.
In my previous post, Open/Closed Principle, I created an interface, IPay, and created two derived classes, FTEPay and ContractorPay. LSP requires each and every inherited class, FTEPay and ContractorPay, be interchangeable. Currently they fall within the LSP guideline, but let’s say, purely hypothetically, HR had originally requested our pay system to allow upper management to receive payments in their Cayman Island accounts. You know for *cough cough* tax incentive purposes. We may have been tempted to add this to our IPay interface:
Writing this abstract method (SendToBigBallerAccount) inside IPay breaks the Liskov Substitution Principle, since full-time employees and contractors won’t have offshore accounts and therefore can’t implement this method. These derived classes are forced to throw an exception for this method, potentially breaking the application since they are no longer interchangeable with our new BigBallerPay class:
BigBallerPay implements new method
FTEPay class throws an exception
How can we get around this without breaking SOLID principles? One way is to further abstract our new method into a separate interface. In this example, we’ll move the Cayman Islands pay method from our IPay and existing inheriting classes to a separate interface, IBallerPay:
Now our BallerPay class is interchangeable with any derived IPay class, without the application having to worry about throwing exceptions. We can also add new pay types,, C-suite executives etc, to our application without worrying about breaking any existing code..
LSP covers many subtle OOP pitfalls when it comes to substituting types. Here are a few links to get you started with ways to spot, avoid, and refactor LSP violations:
- Robert Martin: The Liskov Substitution Principle
- Big Nerd Ranch: What is the Liskov Substitution Principle?
- Wikipedia: The Circle-ellipse problem
Next week we’ll continue our SOLID principles building on what we’ve learned today with ISP: Interface Segregation Principle. See you then!