It’s been a while since OOP/Design Pattern principles have been a topic on this blog, and now is as good time as any. The 1987 OOPSLA keynote address by Barbara Liskov contained what has become known as the Liskov Substitution Principle. In that address and books and papers, the Liskov principle is an OOP one that is part of the more general OOP set of principles. In languages like C++ and Java where you have strongly typed languages, you can declare a variable (property) by the parent data type. Since PHP is flexibly typed, the data type can change, and while that practice [changing data types for a variable] is not recommended, it is certainly possible. As a result, it’s not as easy to explain and illustrate the principle with PHP.
Essentially, the principle holds that,
If a Client is using a reference to a base class it should be able to substitute a derived class for it without affecting the program’s operation
Actually Dr. Liskov said,:
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.
and I was trying to put it in less technical terms.
Why is the Liskov Substitution Principle Important?
You can easily see three reasons that the principle is important:
- If substitutions cannot be made then class hierarchies would be a mess. Whenever a subclass instance is passed as parameter to any method, surprises might occur.
- Without the possibility of Liskov type substitutions unit tests for the superclass would never succeed for the subclass.
- Changes are easier and more predictable because you know what to expect from the superclass.
No doubt you can find more, but for now, the Liskov substitution principle is another way to keep your code loose and reusable and subject to change.
If the Client has an object of a certain type, it should be able to substitute a derived object of the same type. For example, suppose you have an abstract class named Automobile and from that class you have subclasses of Ford, Toyota, and Peugeot. Your Client has an object, $myAuto. The $myAuto object can be any of the subclasses, and if a substitution is made for any one of them everything keeps on working without a problem and the change is unknown to the Client class. So if you substitute a Ford for a Toyota, the Client can work with the objects without having to adjust for the change. What’s more, if you want to add a Fiat or Mercedes Benz class as a subclass of Automobile, the $myAuto object handles it with nary a whimper.
The one caveat is that the subclasses must all honor the contractual conditions of the parent class. So, any methods in the parent class must be functioning in the subclass (aka, derived class.)
Now you may be thinking, So what? If you’re at all familiar with other principles of OOP and Design Patterns, this principle may sound vaguely familiar, but what is the importance of this concept/principle/idea? It is this: Because the Client is unaware of the concrete class that the object may implement, the structure is far more resilient. Not only can the same structure be reused, it can be changed, updated and generally fiddled with without easily breaking anything. (Think of adding more car manufacturers to the Automobile class.) As far as the Client is concerned, as long as the interface rules are followed with the object, everything is hunky-dory. To get started Play the example and Download the source code:
A Simple Practice for PHP Programmers
As a principle, the Liskov Substitution Principle is easy to use. The problem is in demonstrating it in PHP because of the lack of assigned data types to properties. However, using PHP type hinting, you will see how it works using at a classic example where the Liskov substitution principle (LSP) is used.
Explanations of how LSP works often use a rectangle/square example. Typically most programmers see two alternatives to illustrate LSP programming a rectangle and a square; with LSP and without LSP.
Beginning with the geometric observation, all squares are also rectangles, we’re likely to create a structure like that shown in Figure 1:
After all, we can envision a square as nothing more than a rectangle with equal sides. So an operation such as,
is an effective way to make either a square or rectangle. We add the Square class simply to examine the LS Principle; otherwise creating rectangles or squares would be accomplished using the makeRectangle(w,h) method making sure that w and h are equal when making squares. The inheritance path clearly indicates that a Square object IS A Rectangle. (That is, the two have an IS A relationship.) However, because Rectangle is a concrete class if I substitute Rectangle for Square, I’m going to have to change the type. I cannot substitute an instance of Rectangle for an instance of Square. This violates the Liskov Substitution Principle.
Continue reading ‘OOP Principles: The Liskov Substitution Principle’