I am new to design and learning the design principles.
It says deriving square from rectangle is a classic example of violation of Liskov's Substitution Principle.
If that's the case, what should be the correct design?
I am new to design and learning the design principles.
It says deriving square from rectangle is a classic example of violation of Liskov's Substitution Principle.
If that's the case, what should be the correct design?
The answer depends on mutability. If your rectangle and square classes are immutable, then Square is really a subtype of Rectangle and it's perfectly OK to derive first from second. Otherwise, Rectangle and Square could both expose an IRectangle with no mutators, but deriving one from the other is wrong since neither type is properly a subtype of the other.
I believe the reasoning is something like this:
Let's say you have a method that accepts a rectangle and adjusts its width:
public void SetWidth(Rectangle rect, int width)
{
rect.Width = width;
}
It should be perfectly reasonable, given what a rectangle is, to assume that this test would pass:
Rectangle rect = new Rectangle(50, 20); // width, height
SetWidth(rect, 100);
Assert.AreEqual(20, rect.Height);
... because changing a rectangle's width does not affect its height.
However, let's say you've derived a new Square class from Rectangle. By definition, a square has height and width always equal. Let's try that test again:
Rectangle rect = new Square(20); // both width and height
SetWidth(rect, 100);
Assert.AreEqual(20, rect.Height);
That test will fail, because setting a square's width to 100 will also change its height.
Thus, Liskov's substitution principle is violated by deriving Square from Rectangle.
The "is-a" rule makes sense in the "real world" (a square is definitely a kind of rectangle), but not always in the world of software design.
Edit
To answer your question, the correct design should probably be that both Rectangle and Square derive from a common "Polygon" or "Shape" class, which does not enforce any rules regarding width or height.
I don't agree that deriving square from rectangle necessarily violates LSP.
In Matt's example, if you have code that relies on width and height being independent, then it does in fact violate LSP.
If however, you can substitute a rectangle for a square everywhere in your code without breaking any assumptions then you're not violating LSP.
So it really comes down to what the abstraction rectangle means in your solution.
Let's assume we have the class Rectangle with the two (for simplicity public) properties width,height. We can change those two properties: r.width=1, r.height=2.
Now we say a Square is_a Rectangle. But though the claim is "a square will behave like a rectangle" we can't set .width=1 and .height=2 on a square object (your class probably adjusts the width if you set the height and vice versa). So there's at least one case where an object of type Square doesn't behave like a Rectangle and therefore you cannot substitute them (completely).
Its pretty simple :) The more 'base' the class (the first in the derivation chain) should be the most general.
For example shape -> Rectangle -> Square.
Here a square is a special case of a rectangle (with constrained dimensions) and a Rectangle is a special case of a shape.
Said another way - use the "is a" test. A squire is a rectangle. But a rectange is not always a square.