In Friday’s post, I explained and gave examples of SOLID principles. I left out an example of the Liskov Substitution Principle because it deserves its own post. Here’s a recap of the relevant section:
In short, the idea is that objects in a given class hierarchy should be replaceable with their subtypes without modifying the “correctness” of the system. In proper terms, it means that method/class preconditions cannot be strengthened, post conditions can’t be weakened, all exceptions thrown must equally interchangeable, and method signatures should be completely compatible.
So the Liskov Substitution Principle boils down to:
- method/class preconditions cannot be strengthened;
- method/class post conditions can’t be weakened;
- all exceptions thrown must equally interchangeable; and
- method signatures should be completely compatible.
When we actually try to apply this to the real world, it gets complicated. So to begin, we’ll start with one of the first examples many of us saw when we learned about Object Orientation.. Shapes!
To get rolling, here’s a simple Rectangle class:
class Rectangle {
protected $_height = '';
protected $_width = '';
public function __construct($height, $width) {
$this->_height = $height;
$this->_width = $width;
}
public function getArea() {
return $this->_height*$this->_width;
}
}
It doesn’t do anything special but definitely supports the definition of a Rectangle. Next, let’s implement a Square class. Since we know that “all squares are rectangles but not all rectangles are squares,” it implies this class hierarchy:
class Square extends Rectangle {
public function __construct($side) {
parent::__construct($side, $side);
}
}
Unfortunately, we have different method signatures, so we’ve broken requirement 4. So let’s fix that:
class Square extends Rectangle {
public function __construct($height, $width) {
parent::__construct($height, $width);
}
}
But now we might end up with an un-square Square, so we need to validate the sides:
class Square extends Rectangle {
public function __construct($height, $width) {
if ($height != $width) {
throw new Exception("That isn't a square knucklehead!");
}
parent::__construct($height, $width);
}
}
But now we’ve added a new exception and added new preconditions on the input.. breaking requirements 1 and 3.
Unless I’m missing something, LSP is difficult to implement “properly” even with simple class structures. Granted, this is a bit of a contrived example, but it’s the first OO example in most classes, books, and even OO articles out there. Imagine if I had started with the other Shapes example and included a circle or triangle?
If we can’t practice “good” OO Design principles here with less than 20 lines of code, how can we do it in the real world? Is it even reasonable to expect it?
Of course, I might be wrong.. please tell me how. I’d love the insight.




June 6, 2011 at 5:32 pm
Read the first answer for this question.
http://stackoverflow.com/questions/4019748/programming-by-contracts-in-php
From my understanding the pre-conditions can be implemented ( in a simulated manner, the invariants and post-conditions can’t )
June 6, 2011 at 10:48 pm
To quote wikipedia: A square is both a rhombus (equal sides) and a rectangle (equal angles) and therefore has all the properties of both these shapes.
Multiple Inheritance? Nah, just make the constructor more enterprise-ready: A Polygon takes a number of Edges checked by concrete by the derived n-gon.
https://gist.github.com/1011279
June 7, 2011 at 8:51 pm
I think the issue here is that the structure should be driven by the case in point. If you consider shapes, square and rectangle are different things, which behave differently. Imagine you are implementing diagram editor – you’d have them to behave differently, e.g. when resizing. It may be hard to adjust to it since we say "square is a kind of rectangle that …" but in this particular case it is not. We should be cautions applying common language sayings to design structure, sometimes they are very misleading.
June 7, 2011 at 9:06 pm
Stas,
Really good point. But still, the "use shapes to show OO" is a common pattern out there. I just poked around and the most common variation I found was using a Shape Abstract that all others implemented. Then Square wasn’t a child of Rectangle, they just had the same parent.
June 12, 2011 at 5:30 am
I think it just takes a little more effort and thought in determining why you might be adding the code that violates LSP. In your example your validating the shape in the constructor, which is fine, but to satisfy LSP you would probably want to implement a validation function that the Rectangle class also calls in it’s constructor, and handles failures through exceptions as well. This let’s your Square class also validate and throw an exception, and behave just as it’s parent can be expected to behave, following LSP.
June 17, 2011 at 8:49 pm
Hey Casey,
Good and insightful post, indeed. It"e;s valid to point out that the term "e;correctness"e; of a system in LSP always makes reference to the behavior of base types and subtypes. But, it turns out to be that this behavior is defined by the contract (or the API) that the objects expose to client code. However, a constructor is never part of such a contract. What"e;s more, the constructor of a child class might be quite different of the one defined by its parent, (different signatures and implementations); even so, if the behavior of a derivative is at least the same than of the base type, it doesn"e;t violate LSP.
So, the third version of your Square class is correct, as long as the contract that both Rectangle and Square honor is limited to calculating areas (yes, even when constructor signatures are different and preconditions are strengthened in the subtype). Of course, this is a very contrived example, so let"e;s showcase a more realistic one: the constructors of ZF AbstractTable and Table.
Are these violating LSP? Nope, since both AbstractTable and Table expose the same contract to the outside, even when their constructor signatures are distinct and preconditions in the subtype has been strengthened as well.
To sump up: requirements established by LSP must be respected in the contract defined by the abstraction, be it an abstract-concrete class or an interface. Object construction is out of this scope.
Cheers,
Alex
September 11, 2011 at 3:30 pm
Casey, the point you raise in this post was something I once thought as well. I had an "ah-ha moment" when I had a long discussion about the LSP with a C# .NET developer, and he pointed out the same thing that Alex has so eloquently pointed out above.
PHP further enforces these principles because constructors are not subject to method signature changes in PHP, try it.. no E_STRICT will be raised.
Further, think about the following code:
<pre>
$obj = new Rectangle(5,10);
if ($obj instanceof Rectangle) {
// do type safe behavior since $obj already
// exists, and we know it’s minimal contract
// is that of a rectangle
}
</pre>
Object construction can not play a part of that scenario where instanceof becomes important because the subtype must already exist.
Ultimately, your summarization of the LSP is missing a very important word that would make all this very clear. Those rules apply only to subtypes, the wikipedia article makes that pretty clear.
"Contravariance of method arguments in the subtype.
Covariance of return types in the subtype.
…
Preconditions cannot be strengthened in a subtype.
Postconditions cannot be weakened in a subtype. …"
Since subtypes cannot exist pre-construction time (nothing exist yet), therefore constructor signatures are not subject to these rules.
Beyond that, it is interesting that you CAN enforce a constructor signature in PHP interfaces. I am not sure how I feel about that. Generally speaking, I’d say that you should NOT be putting __construct() inside an interface unless for some reason, construction of an object is very much part of the contract being enforced- at which point, you should have some pretty well thought out requirements if that is the case.
-ralph
September 14, 2011 at 7:27 pm
Excellent contribution, Ralph, which makes even clearer to grasp what is actually behind the LSP. Also, I liked the fact you raised up a central concept of Design By Contract: although effectively PHP is quite permissive, as it allows to declare constructors in interfaces (unlike other languages, like Java), this is a bad practice with tons of big, ancient drawbacks over its shoulders. Even if you are a purist worshiper of the Interface Segregation Principle (mea culpa), and use to set a bunch of fine-grained contracts, they never should define the way that an object must be constructed, unless (as you stated above) you have really, really strong reasons to do so.
Cheers,
Alex