I was in a course recently and someone said that it was wrong to have a class hierarchy where you had a base Rectangle class, and then you had a Square class that inherited from Rectangle.
Now, I know that in general people went too hog-wild with inheritance when they first started using object-oriented programming. The canonical wisdom from the Design Patterns book is that if you have a class for an encryption chip and then you have different kinds of encryptors, rather than have a base EncryptionChip class and then have AesEncryptionChip and Rot13EncryptionChip and etc, all of which inherit from EncryptionChip, you should instead design EncryptionChip to contain an Encryptor class which does the actual encryption (this is known as containment or aggregation). That way you can construct an EncryptionChip with the specific Encryptor you need, but the specific Encryptor doesn’t need to know anything about the EncryptionChip (as opposed to a subclass, which does need to know about its parent class). In OO terms they are more loosely coupled, which means it’s easier to make changes if needed. If you design the Encryptor class so that EncryptionChip only deals with an abstract base class, and the concrete Encryptors inherit from that base class, then you have the design pattern known as the Strategy.
But in the case of Square inheriting from Rectangle, I don’t see what is wrong. I suppose you could have the Rectangle class have a member that did some sort of ValidateSize() function, which for the Square would fail if the x and y dimensions were unequal, but that seems over-designed. Anyway, the argument against Square inheriting from Rectangle was not about the internals of implementation, but rather about the external behavior that this Rectangle-that-is-really-a-Square would present to the world.
The argument specifically relates to the Liskov Substitution Principle. The LSP informally states that if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering the program. In this specific case, if Square is a subtype of Rectangle, then objects of type Rectangle in a program may be replaced with objects of type Square without altering the program.
At first glance this doesn’t seem like a problem for Square. After all if you are using a Rectangle somewhere and you replace it with a Square (which inherits from Rectangle and thus supports at least the same public interface) then everything should work. And this seems “right”, because in a mathematical sense a square IS a rectangle, so an object-oriented design rule that disallows a Square from being a Rectangle would seem to indicate a bad rule.
But, the Liskov argument against Square: Rectangle is that if you were to set the parameters of something you thought was a Rectangle, and you happened to set x != y, then it would work for a base Rectangle, but not for a Square (since the Square presumably fails in some way in that situation). Thus, designing Square to inherit from Rectangle is wrong because a Square CANNOT be substituted for a Rectangle.
I’m not buying it. First of all, as I said, a square is a rectangle. But also, if you dig deeper into Liskov (meaning you read the Wikipedia entry), it defines it formally as:
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.
And to me there is nothing in there that disallows Square from inheriting from Rectangle. Everything that could probably be true about a rectangle is certainly true about a square. I don’t consider “Well, x and y MAY be different” to be a provable property–a rectangle says nothing about the relationship between x and y, so there is nothing for a square to contradict.
Further, if you look at Liskov from a Design by Contract perspective, it states that preconditions cannot be strengthened in a subclass, and post-conditions cannot be weakened. Which is fine: in moving from Rectangle to Square you are strengthening a post-condition (by adding one that says x must equal y).
The book The Pragmatic Programmer also briefly touches on Liskov, defining it (on pp. 111-112) as:
Subclasses must be usable through the base class interface without the need for the user to know the difference.
I don’t see a problem here; you would certainly be able to use a Square through the Rectangle base class. I mean, your Rectangle.SetXY() method might fail, but any method you call might fail; if some code handed a Square off to other code that was using it as a Rectangle, it’s presumably because they wanted something to fail in that situation (while allowing that other code to remain loosely coupled to themselves). The Pragmatic Programmer also says (in reference to Liskov and DBC) “A subclass may, optionally, accept a wider range of input, or make strong guarantees.” Which is exactly what a Square is doing, making stronger guarantees.
So, my conclusion is that the Liskov argument, in this situation, is wrong, and (at least from that perspective) it’s perfectly fine for Square to inherit from Rectangle. The amazing thing, of course, is that all this is so un-clear, so many years after object-oriented programming came about.
Consider the abstract base class or interface (whether something is an interface or abstract class is an implementation detail rather irrelevant to the LSP) ReadableRectangle; it has read-only properties Width and Height. It would be possible to derive from that a type ReadableSquare, which has the same properties but contractually guarantees that Width and Height will always be equal.
From ReadableRectangle, one could define concrete type ImmutableRectangle (which takes a height and width in its constructor, and guarantees that the Height and Width properties will always return the same values), and MutableRectangle. One could also define concrete type MutableRectangle, which allows the height and width to be set at any time.
On the “square” side of things, an ImmutableSquare should be substitutable for both an ImmutableRectangle and a ReadableSquare. A MutableSquare, however, is only substitutable for a ReadableSquare [which is in turn substitutable for a ReadableRectangle.] Further, while the behavior of an ImmutableSquare is substitutable for an ImmutableRectangle, the value gained by inheriting a concrete ImmutableRectangle type would be limited. If ImmutableRectangle were an abstract type or interface, the ImmutableSquare class would only need to use one field rather than two to hold its dimensions (for a class with two fields, saving one is no big deal, but it’s not hard to imagine classes with a lot more fields, where the savings could be significant). If, however, ImmutableRectangle is a concrete type, then any derived type would have to have all the fields of its base.
Some types of square are substitutable for the corresponding types of rectangles, but a mutable square is not substitutable for a mutable rectangle.
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).
I believe that OOD/OOP techniques exist to enable software to represent the real world. In the real world a square is a rectangle that has equal sides. The square is a square only because it has equal sides, not because it decided to be a square. Therefore, the OO program needs to deal with it. Of course, if the routine instantiating the object wants it to be square, it could specify the length property and the width property as equal to the same amount. If the program using the object needs to know later if it is square, it needs only to ask it. The object could have a read-only Boolean property called “Square”. When the calling routine invokes it, the object can return (Length = Width). Now this can be the case even if the rectangle object is immutable. In addition, if the rectangle is indeed immutable, the value of the Square property can be set in the constructor and be done with it. Why then is this an issue? The LSP requires sub-objects to be immutable to apply and square being a sub-object of a rectangle is often used as an example of its violation. But that doesn’t seem to be good design because when the using routine invokes the object as “objSquare”, must know its inner detail. Wouldn’t it be better if it didn’t care whether the rectangle was square or not? And that would be because the rectangle’s methods would be correct regardless. Is there a better example of when the LSP is violated?
One more question: how is an object made immutable? Is there an “Immutable” property that can be set at instantiation?
I found the answer and it is what I expected. Since I’m a VB .NET developer, that is what I’m interested in. But the concepts are the same across languages. In VB .NET you create immutable classes by making the properties read-only and you use the New constructor to allow the instantiating routine to specify property values when the object is created. You can also use constants for some of the properties and they will always be the same. From creation forward the object is immutable.
Clinton put it the best: it depends on what the definition of is is
The unshakable intuition that a square is a rectangle comes from our mathematical training. Mathematical objects are immutable and identity-less. If your program indeed is modeling square and rectangle objects in the mathematical sense, then Square should be a subtype of Rectangle, and they should be immutable. Any mathematical operations that applicable to Rectangle are applicable to Square.
However, your program may not be modeling mathematical objects. Maybe you are modeling graphic screen objects. There are mathematical aspects in them, but then there are more. Then we are in a mess. Maybe it’s better to design Rectangle as subtype of Square, considering all the operations that you want to put on them. Then it’s completely against our mathematical intuition, and we do not want that kind of confusion in our design.
This is a horrible truth: OOP is meh. You may have thought that some super smart people did some grandiose research and came up with this omnipotent programming model. For every puzzle there is a perfect solution, you just don’t know it because you haven’t become good enough in understanding this divine revelation. People argue about OOP with more zeal than religious enemies, throwing big words and abstract concepts at each other, quoting principles and conventions from ancient texts which nobody really understands.