Uh oh! There’s danger! Time for the Wonder Twins to activate their super powers.
for twin in wonder_twins()
for power in twin.powers()
power.activate()
...
Fatal Error: Subtype "AnimalMagnetism" cannot be activated in class "SaveTheWorld", line ...
Humanity is destroyed because another developer hasn’t heard of the Liskov Substitution Principle. Let’s take a closer look at it.
The Wikipedia article linked to above has this, er, succinct description:
Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.
The article goes on a bit more to explain what’s going on, but I confess that at first my eyes glazed over for a moment. It’s not that this description is wrong, it’s just so terse that many developers don’t get it.
I recently had a developer tell me “yeah, that’s a nice idea in theory”. And he’s right. It is a nice idea in theory. It’s an even nicer idea when put in practice. Those who understand the Principle might roll their eyes at the programmer’s statement, but he’s actually a good programmer who I’d be happy to work with on any project. It’s just that many programmers learn OO without being exposed to many of its fundamental rules.
In the above pseudo-code, imagine that there is a “Power” abstract class. A Wonder Twin might have an “AnimalMagnetism” implementation of “Power” which lets him get free drinks in nightclubs. Very useful. However, another developer comes along and envisions a weaker subclass of this power, but it’s always on and thus does not need to be activated, so they have the activate() method throw an exception. Here’s one (wrong) way to work with this:
for twin in wonder_twins()
for power in twin.powers()
if power.type != "AnimalMagnestism"
power.activate()
The problem here is that you now have to search through your code for every attempt to activate a power and write a special case for it. Even if this is the only place, you run the risk of updating it every time you add a new power. In fact, though this statement will seem a bit strong for some, the existence of conditionals (if/then) in OO code is something of a code smell. Once I realized this, I found that by going through my OO code and looking at “if” statements, I often found subtle design flaws.
One better method of dealing with this problem may be to simply have the weaker “AnimalMagnetism” subclass ignore calls to the activate() method (simply having a stub method with no body might be sufficient).
If you really feel that a subclass is needed for something, there are some points to remember. If you are extending it, that’s fine. If you’re altering the behavior of an existing method, you must not have tighter restrictions on what arguments can be accepted. In other words, if the number 3 is a valid argument to a method, you shouldn’t suddenly disallow the number 3. You’re going to get frantic 3:00 AM phone calls wondering why the boss can’t order 3 drinks.
The return arguments, if any, must also be considered. You can have tighter restrictions on what you emit, but you must not expand the domain. In other words, if the calling code expects no negative numbers, don’t start returning them or else the calling code’s behavior is likely to be unpredictable.
In other words, any code dealing an object of a type X should never have to know or care whether or not they are dealing with a subtype of X.
Go back and reread the Wikipedia article. If you didn’t understand it before, it might make more sense now. However, remember that the only hard and fast rule is that there are no hard and fast rules. While the Liskov Substitution Principle is incredibly important, sometimes we find ourselves coding into a corner due to deadlines. Just don’t forget to go back and refactor later.
Side note: some argue that Liskov means that you must never override an existing method. I think that’s a little extreme and not what was intended.


Subclasses can have more restrictive arguments to methods, it just that the object still has to act like the super class. For instance, there's an Integer class and a PositiveInteger subclass. The subclass obviously has to restrict it's arguments since it only deals with a subset of the numbers, but you should still be able to substitute a PositiveInteger object for an Integer object. Their values have the same properties, although the classes deal with different sets of them. That's the point of many subclasses, in fact. :)
Hey, you already heard about Contract Programming, didn't you ?
In Contracts, when you override a method, you can allow more values to be accepted as input parameters. But, you must restrict the return value or output values of the method.
I mean, PreConditions don't need to be satisfied but all the PostConditions should be satisfied.
[code]
Time::Set(int hour, int minute, int second)
{
// PRECONDITIONS
assert( 0 <= hour && hour < 24 );
assert( 0 <= minute && minute < 60 );
assert( 0 <= second && second < 60 );
_hour = hour;
_minute = minute;
_second = second;
// POSTCONDITIONS
// None in this case (sorry) :)
}
// (SpentTime is derived from Time)
SpentTime::Set(int hour, int minute, int second)
{
// PRECONDITIONS are weaker
assert( 0 <= hour );
assert( 0 <= minute );
assert( 0 <= second );
_hour = hour;
_minute = minute;
_second = second;
}
[/code]
Is a Path a kind of String? Yes. Can I choose to implement a Path class as a subclass of String.
If we follow the LSP then the answer to the second question is no and I must delegate all string-like methods. Why? Using Ruby as my example, consider the String#+ method versus our theoretical Path#+ instance method. With plain strings 'foo' + 'bar' results in 'foobar', but Path.new('foo') + Path.new('bar') results in 'foo/bar'.
Then consider that String + Path results in a String, but Path + String results in a Path.
If I've violated LSP (and I'm not entirely sure that I have) then I don't care, because I'm always going to get the expected behavior - a string when I want one or a Path when I want that, depending on what context I've used it.
I had a conversation on this a while back on my blog (click my name to read more about it), and there are all kinds of issues that are being whitewashed over here.
The biggest is that you act as though the only difference two methods might have are the values you accept and the values you produce. But since you're talking about a language with side effects, that means that your side effects (a.k.a. behaviors) have to be the same -- you can add new side effects, but you can't replace them. This basically means that you can add new functionality, but you can't ever remove/replace existent functionality.
Another problem is what that definition means when it says "a property provable about". Most code isn't written to look like a mathematical proof -- most of it doesn't even really have a specific communication of the API. So developers usually don't have the foggiest idea what is "provable about" their code, they just know how they're using their code. And that gets into a lot of sticky situations.
For instance (and I get into this more on my blog), consider a 2D matrix class, and a symmetric 2D matrix subclass. Is it "provable about" the matrix class that assigning [a][b] doesn't change any other element on the matrix? If so, then the symmetric 2D matrix subclass is a violation of the LSP. If not, then it is a just fine subclass. Of course, in most code bases, there's no clear answer to that question: the answer is really whether or not someone has already written code that depends on that part of the contract being true.
That's a just fine theory about classes and subclasses, but it's pretty limited in practice beyond the implementation suggestions you've laid out in this article.
Of course, it's no surprise that the theory falls a bit short in practice. Theory is a lot closer to practice in theory than it is in practice. ;D
LSP is good at exposing faulty abstractions like the path is a string example provided above.
A path is not a string, it is a path. A string is just representation. If you need to rely on a path you should make something called path which holds parts of the path. You shouldn't rely on a crappy regular expression to verify if something is a path, do the real thing.
What? No "can"?
for twin in wonder_twins()
for power in twin.powers()
if power.can('activate')
power.activate()
Although I would think that it's the twins that activate the power and not the power that activates itself:
for twin in wonder_twins()
for power in twin.powers()
if ! twin.is_activated(power)
twin.activate(power)
And if some super-villain somehow disables activation of some power, maybe:
for twin in wonder_twins()
for power in twin.powers()
if twin.is_activatable(power) and ! twin.is_activated(power)
twin.activate(power)
Hmm, I've thought way too much about this :-)