It's a lot more simple than that quote makes it sound, accurate as it is.
When you look at an inheritance hierarchy, imagine a method which receives an object of the base class. Now ask yourself, are there any assumptions that someone editing this method might make which would be invalid for that class.
For example originally seen on Uncle Bob's site (broken link removed):
public class Square : Rectangle
{
public Square(double width) : base(width, width)
{
}
public override double Width
{
set
{
base.Width = value;
base.Height = value;
}
get
{
return base.Width;
}
}
public override double Height
{
set
{
base.Width = value;
base.Height = value;
}
get
{
return base.Height;
}
}
}
Seems fair enough, right? I've created a specialist kind of Rectangle called Square, which maintains that Width must equal Height at all times. A square is a rectangle, so it fits with OO principles, doesn't it?
But wait, what if someone now writes this method:
public void Enlarge(Rectangle rect, double factor)
{
rect.Width *= factor;
rect.Height *= factor;
}
Not cool. But there's no reason that the author of this method should have known there could be a potential problem.
Every time you derive one class from another, think about the base class and what people might assume about it (such as "it has a Width and a Height and they would both be independent"). Then think "do those assumptions remain valid in my subclass?" If not, rethink your design.
A WordSorter
is-a WordBank
, so code that works with a WordBank
should also work when a WordSorter
is used instead of a WorkBank
. On the other hand, a WordSorter
is-not-a SomeWordBank
. The compiler won't even let you use a WordSorter
in place of a SomeWordBank
, so the issue does not even begin to arise.
There might be a LSP violation, but doesn't appear to be from the minimal specification you've given. Does WordSorter
guarantee, for example, that one can add arbitrary strings to it and retrieve them all in the same order later? Then sorting the words would indeed break the that contract, and code that works for "correct" WordBank
s can be broken by substituting a WordSorter
. Whether there is such a guarantee is impossible to tell from the minimal UML diagram you've shown. If, for example, WordBank
's contract says all words which are added are included in the result of getWords
, then:
bank.add(w);
AssertContains(bank.getWords(), w);
should always work. That code would break if bank
was a WordSorter
, and it's WordSorter
's fault for breaking the contract and hence violating the LSP. But if WordBank
offers no such guarantee, then the above code is in the wrong (in the same way asser x*y == 42
will usually fail) and WordSorter
is a perfectly well-behaved subclass.
Best Answer
Yes, it is a violation of the LSP. Liskov Substitution Principle requires that
Your example breaks the first requirement by strengthening a precondition for calling the
Close()
method.You can fix it by bringing the strengthened pre-condition to the top level of the inheritance hierarchy:
By stipulating that a call of
Close()
is valid only in the state whenCanClose()
returnstrue
you make the pre-condition apply to theTask
as well as to theProjectTask
, fixing the LSP violation: