In my previous post, I’ve used the example of squares and rectangles to illustrate the importance of definitions. It is clear that, from a geometric point-of-view, a square is indeed a kind of rectangle. However, as my friend Arnon has noticed, this may be problematic from an object-oriented perspective.
Let’s say that we organize the classes Square and Rectangle in a hierarchy so that Square inherits from Rectangle. This reflects exactly the geometric relationship between them, since inheritance is commonly used to represent the is-a-kind-of relationship.
Now suppose that we have a photo-enhancement application with a feature to take a picture and place a border around it. It would be natural to use a Rectangle as the border, and have a function that would receive as parameters the original Picture and a Rectangle and return a new Picture with a border:
Picture addBorder(Picture picture, Rectangle border)
This function would be responsible for adjusting the height and width of the border to fit the dimensions of the picture.
What is the problem now? Because Square is a subclass of Rectangle, we could pass an instance of Square as the border parameter. But then, if the picture is not a square, it should not be possible to change the border’s height and width, or otherwise the border would no longer be a square.
The conclusion is that a square cannot be used in every situation in which a rectangle can be used, and therefore the class Square should not be a subclass of Rectangle.
But what is the root of the problem?
In the world of geometry, figures are static objects with unchangeable properties. But in the world of object-oriented programming (OOP) objects also have functionality that implement a well-defined dynamic behavior.
Structurally, Squares are a specialization of Rectangles, since both have four sides and all internal angles right. A Square is special because it has its height equal to its width.
Functionally, Squares do not behave like Rectangles. A Rectangle should have independent methods to change its height or its width. Clearly, these methods would not be appropriate for a Square.
Inheritance should be used only if the is-a relationship is both structural and functional.