What is The Liskov Substitution Principle (in PHP)
When a developer extends a base class, the methods of that base class must be viewed with regard to its public behaviour. The developer extending the base class should assume that any other developer using their derived class does not have access to the internal workings of their code, but only the method signatures of the base class.
Based on those assumptions, we would be able to substitute any derived class for any other (where the base class is referenced) without any unexpected behaviour.
Let's look at a violation of the open-closed principle (OCP) and how the solution to it, is linked to the Liskov substitution principle.
Our Page
class builds page elements using the buildElement
method, but it violates OCP because it is not closed for change. If we wanted to build other element types, e.g.: fieldset, we would have to change the buildElement
method.
So let's update our buildElement
method to comply with the open-closed principle and then discuss how LSP fits in.
Our code now complies with OCP and LSP.
How do we comply with LSP?
The build
method of the DivElement
and SpanElement
class both behave as we expect it to i.e we can pass a DivElement
or a SpanElement
to the buildElement
method without any unexpected consequences.
We can substitute any derived class (like a future ParagraphElement
) and the buildElement
method will behave as we expect.
LSP with unexpected behaviour
If a developer were to guess the purpose of the buildElement
method, we might guess that its purpose is to construct or put together the various pieces of the element i.e to get the element ready for rendering.
We wouldn't expect the buildElement
method to behave differently depending on which derived class is passed to it.
To comply with the Liskov substitution principle, any new build
method must not add or remove from the core purpose of what we reasonably assume the build method is. Suppose another developer built the ParagraphElement
as shown below:
The above build
method will fail when buildElement
calls it, buildElement
checks the properties
before returning the output. But the build
method of ParagraphElement
sets the properties
to null. It does not behave as we'd expect the base class method to behave.
In a similar way, if the build
method were to comply with LSP, it must not remove the actual building functionality of it's build method (we do expect the build
method to build something!)
LSP in Laravel
Let's look at an example of LSP in Laravel.
The Laravel example above has a base class/interface ConnectorInterface
which is implemented by RedisConnector
and DatabaseConnector
. For our code to comply with LSP, any call to connect
should work withs any derived class. In this case, the call to connect: $this->getConnector()->connect($config)
does not check the type of the derived class, it is compliant with the Liskov substitution principle.
Key Takeaways
- By implementing the Liskov substitution principle, developers can program to the base class or interface as they can be confident that all derived classes behave as they reasonably expect.
- The Liskov substitution principle is violated when our reasonable assumptions of a method are either unmet or over exceeded i.e the method does too little or does more than what we expect.
Further Reading
This article is part of a series on SOLID in PHP, the previous article is The Open-Closed Principle.