A Square Is Not a Rectangle
The following example, taken from an introductory text in object oriented programming, demonstrates a common flaw in object oriented design. Can you spot it?
public class Rectangle {
private double width;
private double height;
public void setWidth(double width) {
this.width = width;
}
public void setHeight(double height) {
this.height = height;
}
public double getHeight() {
return this.height;
}
public double getWidth() {
return this.width;
}
public double getPerimeter() {
return 2*width + 2*height;
}
public double getArea() {
return width * height;
}
}
public class Square extends Rectangle {
public void setSide(double size) {
setWidth(size);
setHeight(size);
}
}
(I’ve changed the language and rewritten the code to protect the guilty.)
There are actually several problems here. Thread safety is one, but let’s assume the class doesn’t need to be thread-safe. Another is that it’s possible to give the sides negative lengths. That we could fix with a couple of judiciously thrown IllegalArgumentExceptions. However the problem that most troubles me is demonstrated here:
Square s = new Square();
s.setWidth(5.0);
s.setHeight(10.0);
In object oriented programming, it is necessary that a subclass be able to fulfill the contract of its superclass. In this case, that means the square has to respond to setHeight()
and setWidth()
calls. However doing so enables the square to violate the nature of a square. A square cannot stand in for a rectangle.
You can try to work around this by overriding the setHeight()
and setWidth()
methods. For example, one might call the other:
public class Square extends Rectangle {
public void setWidth(double width) {
super.setWidth(width);
super.setHeight(height);
}
public void setHeight(double height) {
super.setWidth(width);
super.setHeight(height);
}
public void setSide(double size) {
super.setWidth(width);
super.setHeight(height);
}
}
However this is fundamentally unsatisfying because there is no reasonable expectation that calling one of setHeight()
on a Rectangle
object will also invoke the setWidth()
method or vice versa. The contract of the Rectangle
class is that you can set the width and the height independently, and the Square
subclass violates that. Setting width when you’re setting height is an unexpected side effect. It violates the single responsibility principle1.
We could instead just forbid setHeight()
and setWidth()
completely by throwing UnsupportedOperationException
:
public class Square extends Rectangle {
public void setWidth(double width) {
throw new UnsupportedOperationException();
}
public void setHeight(double height) {
throw new UnsupportedOperationException();
}
public void setSide(double size) {
super.setWidth(width);
super.setHeight(height);
}
}
However, this is really just a louder way of warning the client that the Square
class does not fulfill the contract of the Rectangle
class. It doesn’t address the fundamental problem that, in object oriented terms, a square is not a rectangle. The geometric nature of a square is incompatible with the object-oriented definition of a rectangle given above.
There is, however, a way out of this conundrum. Our problem only arises because of the setter methods. If constructor were used instead, and no setters were exposed, then it would be possible to make a square a subclass of rectangle without violating any contracts. For example,
public class Rectangle {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public void getHeight() {
return this.height;
}
public void getWidth() {
return this.width;
}
public double getPerimeter() {
return 2*width + 2*height;
}
public double getArea() {
return width * height;
}
}
public class Square extends Rectangle {
public Square(double size) {
super(size, size);
}
}
As long as the Rectangle
class is immutable, we can define subclasses that are limited to a particular subset of rectangles, such as squares. This is one more reason to prefer immutability. To the extent possible, define the public interface in terms of what an object is and what it does rather than what you can do to it. However that’s not always possible, and in those cases you need to be extremely careful around inheritance. Otherwise constraints can be violated when you least expect, thus introducing subtle and potentially dangerous bugs in your code.
1 Actually the single responsibility principle is usually understood to apply to classes, but it’s even more critical that it apply to methods. each method should do exactly one thing and one thing only. Side effects should be avoided.
September 11th, 2009 at 7:06 am
This is the classic example of the LSP (Liskov Substitution Principle) which says that subclasses must be substitutable for their base classes. You make the point that mutable squares cannot be substituted for mutable rectangles. However, you suggest that the problem can be solved by making the classes immutable. In truth a problem remains. The immutable Square, by virtue of it’s inheritance from the immutable Rectangle has two member variables. Yet it needs only one. The Rectangle class gives those two variables different names (e.g. width, height). Those names are inherited into the immutable Square and are misnomers at best.
The truth is that Squares and Rectangles, even immutable Squares and Rectangles, ought not be associated by inheritance.
This flies in the face of a rather large body of casual OO thought. People want to use inheritance wherever they can apply ISA. A Square ISA Rectangle, therefore we want to derive Square from Rectangle. But no. The class Square is not a square, it is a program that represents a square. The class Rectangle is not a rectangle, it is a program that represents a rectangle. And just as the two lawyers in a divorce case are not divorcing each other, the representatives of two geometric shapes do not share the relationships of those shapes. The fact that a square is a rectangle does not mean that their representatives share the ISA relationship.
A pox on the ISA relationship. It’s been misleading and damaging for decades. Inheritance is not ISA. Inheritance is the redeclaration of functions and variables in a sub-scope. No more. No less.
September 11th, 2009 at 7:57 am
The issue is even deeper than suggested.
I first encountered the square–rectangle (or circle–ellipse) problem head on in production code in 1993 (and yes, being a graphics-based system, it really was to do with shapes!), not long after learning about LSP. I was tasked with adding editing functionality to a system whose graphical model was previously taken from a different system (a GIS system, to be precise). After I added this functionality, we got some strange behaviour, particularly when resizing groups that included form-preserving shapes, such as circles and squares. What had happened?
Most descriptions of LSP focus on inheritance, whereas LSP is actually about subtyping, which is more generally about relationships between types and their usage in a particular context. It so happens that inheritance is a mechanism for relating types, and LSP can guide us in that, but it’s not the only game in town. It applies to other forms of polymorphism, such as the classification by Cardelli and Wegner: inclusion polymorphism (extends in Java), parametric polymorphism (generics, but nothing so random and clumsy as generics in Java), overload polymorphism (particularly relevant when a language supports operator overloading) and coercion polymorphism (in other words, implicit conversions to other types, which is something that you can do in C# and C++, but not Java). In all these cases, LSP applies because it offers guidance on the principle of least astonishment when mixing types that at one level are conceptually interchangeable.
But these are not the only categories or contexts of substitutability. What is missing from most descriptions of LSP gloss over an important qualification: substitutability is not some absolute idea, it is always with respect to something, and often people forget to qualify what it’s with respect to — and it’s not so much a matter of forgetfulness, it’s normally that they aren’t aware that there is something to qualify. One of the various forms of polymorphism is an example of such a qualification. But the one of most interest and relevance here is the notion of substitutability with respect to change. When you ask that question, it makes clear what is at the root of the square–rectangle problem: we’ve not actually considered in what ways they are (and are not) substitutable. We’ve failed to understand how we are using the types we’ve created (matching setters to getters is a common smell that hints at this) and what invariants each type holds and what invariants span the system of types.
In mathematics, it is easy to say that a circle is an ellipse, but when we transform one shape into another, we are doing just that: the original shape remains unchanged. It’s a functional transformation that results in a new shape. So, we’ve used the relationship from one system of description — mathematics — in another system that is quite different — objects with identity, state and the potential for state change. And we’ve discovered that using incompatible models didn’t work out too well. Which, on reflection, isn’t all that surprising.
The production system I mentioned before went from one that did not admit state changes — and could therefore support something close to the mathematical description — to one that did — and gave surprises. It took a while for me to realise that it was not a code thinko but a deeper issue of using incompatible models without realising that there was more than one model at play. Once you figure out that the issue concerns substitutability with respect to change, it’s possible to fix the problem (there are half a dozen different approaches, each with different trade-offs and suitability in different contexts). It’s worth noting that if you want them to remain distinct types, you can make the types substitutable with or without inheritance (depending on language), but that if you use inheritance the use of full immutability is not a foregone conclusion. Some state changes are perfectly compatible (e.g., translation).
September 11th, 2009 at 8:04 am
Actually a (immutable) Square IS a Rectangle, and it should be modeled in OO as such! The problem is in this simplistic model that people make, it does not not reflect a true square!
You must use interfaces to solve this problem… (Example in C++)
class IPolygon {
public:
virtual int getNumberOfSides() = 0;
virtual int getSideLength(int side) = 0;
virtual int getArea() = 0;
};
class IQuadrilateral : public IPolygon {
public:
virtual int getNumberOfSides() { return 4; }
// may add getDiagonal, getCenterOfMass, etc...
};
class IRectangle : public IQuadrilateral {
public:
virtual double getSideLength(int side) { return (side%2==0) ? this->getWidth() : this->getHeight(); }
virtual double getWidth() = 0;
virtual double getHeight() = 0;
virtual double getArea() { return this->getWidth() * this->getHeight(); }
};
class ISquare : public IRectangle {
public:
virtual double getSideLength() = 0;
virtual double getSideLength(int side) { return this->getSideLength(); }
virtual double getWidth() { return this->getSideLength(); }
virtual double getHeight() { return this->getSideLength(); }
virtual double getArea() { return this->getSideLength() * this->getSideLength(); }
};
class Rectangle : public IRectangle {
public:
Rectangle(double p_width, double p_height) : width(p_width), height(p_height) {}
virtual double getWidth() { return this->width; }
virtual double getHeight() { return this->height; }
protected:
double width, height;
};
class Square : public ISquare {
public:
Square(double p_side) : side(p_side) {}
virtual double getSideLength() { return this->side; }
protected:
double side;
};
I think you can even add mutability if you play with constraints… (i.e., if you change the width, you also change the height, as said before… this is not always bad… think about changing the side of a square selection in a paintbrush app., if you reduce the width, it automatically reduces the length and vice-versa).
September 11th, 2009 at 8:07 am
@Uncle Bob
Agreed IS A is not and should not the only indicator that two classes should use inheritance.
I would argue though that the understanding of IS A is only specific to how a program intend on using those classes. It’s well possible that sometimes a square is a rectangle, but sometimes it doesn’t.
It depends on what is being modeled.
Daniel
September 11th, 2009 at 8:16 am
A is-a B indicates that it is possible to use an A where a B is expected, i.e., that some implicit conversion exists that can convert an A into a B.
Subtyping is just one form of implicit conversion, and not one I recommend using.
September 11th, 2009 at 8:59 am
Everything you say could be said for a Golden Rectangle, rather than a square. I think “a golden rectangle is not a rectangle” is as equally true as ” a square is not a rectangle” i.e. not at all. Going through and substituting(!) Golden Rectangle for Square in your text should be quite revealing in where “common sense” is leading people astray. The problem is not that the square is immutable, where the rectangle is not. The problem is the assumption that the rectangles two sides are independent. A square cannot meet that requirement, nor can a golden rectangle, a standard 16:9 video frame or anything else with a set ratio of sides
Also, your third code example (starting “public class Square extends Rectangle”) has 3 copy-paste errors each function should use only one of height, width or size.
September 11th, 2009 at 9:57 am
Just a quick clarification, Ricky: I think you meant “subclassing” when you said “subtyping”. The two terms are not necessary substitutable for one another :^)
September 11th, 2009 at 10:03 am
Small distraction:
this should not return void
public void getHeight() {
return this.height;
}
September 11th, 2009 at 4:25 pm
Kevlin,
My point applies to either subclassing or subtyping.
September 11th, 2009 at 5:25 pm
Ricky, a clarification: a conversion is not necessarily required for A to be used where B is expected. In your first sentence everything before “i.e.” is fine, but what follows is highly dependent on programming language mechanism. For example, it is true for subclassing/derivation in a number of statically typed systems, but is not the case for dynamically typed systems and is not necessarily the case with parameterized types.
September 12th, 2009 at 3:57 am
Kevlin,
A conversion that does nothing is still a conversion. Perhaps I should have used the term function, then it’d be less of a jump to point out the identity function.
September 12th, 2009 at 10:22 am
As pointed out by “dave”, a square is a just rectangle with an additional constraint.
The idea that subtypes should not introduce new constraints seems highly impractical to me.
The real problem is not the existence of new constraints in subclasses but rather that the fine-grained mutation API provides no natural place to enforce such constraints. This can be is solved in one of two ways:
Make the mutation coarse grained, i.e replace the setWidth and setHeight by
setDimensions(int width,int height)
or more generally,
set(RectangleSpecification spec))
This allows overrides of the coarse grained setter to introduce additional constraints.
Keep the fine-grained setters but introduce a transition point in the API at which point constraints
are checked (this is really to implement the Recltangle and its Specification in the same class),
e.g my favorite variant:
/** Prevent further changes of this. An exception is thrown if the object state is invalid. */
freeze();
So the pox should not be on the subclassing but on overly naïve ideas about state assignment…
September 13th, 2009 at 6:23 am
ISA is useful when trying to model real world relations to make class hierarchies intuitive, but classes are metaphors, and metaphors, if extended too far will break. Trying to build great towers of logic seems like a noble goal, but can quickly become a lofty task of futility.
There is a context and a usage to these squares and rectangles. Any rectangle class will not serve all purposes. If I bend a rectangle around a parallell line shifted in a 3rd dimension, I might get a cylinder. Would I want to extend the Rectangle class to express this? Only if I get paid by the levels of inheritance.
Using type to denote an ephemeral quality seems to me to be extremely silly. The whole point of a square subclass would be for it to enforce its “squaredness” and if the simplest way to do that is to violate the single responsibility principle, so be it. Practicality over principles.
September 14th, 2009 at 4:03 am
It’s a silly example though. A square, or a rectangle, is a value object, so all this goes away when you realise that.
Think about it, you change the side of a square/rectangle, you have a different square/rectangle, not the same one with a different side.
September 14th, 2009 at 8:22 am
And what about the rectangle whose sides happen to be the of same length? It’s clearly a square yet it doesn’t have the Square type. Very silly indeed.
September 14th, 2009 at 4:29 pm
is_square(self):
”’ coz a square is not a new object but a rectangle with a property ”’
return self.width == self.height
also, markup-fail
September 14th, 2009 at 4:48 pm
hmm … your first workaround should throw an exception:
public void setWidth(double width) {
super.setWidth(width);
super.setHeight(height); // error
}
September 14th, 2009 at 4:50 pm
Oleg Kiselyov’s pages on Subtyping, Subclassing, and Trouble with OOP are a must read, and contain proper discussion of the points raised above.
September 14th, 2009 at 5:08 pm
“And what about the rectangle whose sides happen to be the of same length? It’s clearly a square yet it doesn’t have the Square type. Very silly indeed.” – A square is still a rectangle, so I don’t understand your point. The square type is an arbitrary label. Could be called the X Type or the Triangle type. A compiler shouldn’t “know” how to derive types outside of the type signature and defined relationships. Auto-derivation of type from state is not silly or simple since you cant extend at runtime, but you can recast backward (in Java). Let’s first correct the implementation problem of inconsistent behavior.
public class Square extends Rectangle {
public Square(double size) {
super(size, size);
}
public void setWidth(double width) {
super.setHeight(width);
super.setWidth(width);
}
public void setHeight(double height) {
super.setHeight(height);
super.setWidth(height);
}
}
There we go. Subclassing is a tool, but not the only tool to do type switching. Who decided that square should be it’s own class? Square is a runtime check since you can _change_dimensions_ without any additional requirement. When you look at a rectangle, you can’t tell it’s a square without a measurement (your brain does this most of the time by eyeballing of course). There’s no reason to make a square class when it’s still a rectangle, with an additional check. Some people believe that if it can identify itself as a square, it should exhibit square behavior (equilateral length/width scaling). Others expect it to act like a rectangle with a way to see if it’s a square. Those are different behaviors with overlapping concerns. Since the author wanted to determine it at construction, we can do something like this:
public class Rectangle {
private double width;
private double height;
private boolean beSquare = false;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public Rectangle(double size) {
this.width = size;
this.height = size;
this.beSquare = true;
}
public double getHeight() {
return this.height;
}
public double getWidth() {
return this.width;
}
public boolean isAsquare(){
return (beSquare || this.width == this.height) ? true : false;
}
public void setHeight(double height){
this.height = height;
if(this.beSquare)
this.squareRule(height);
}
public void setWidth(double width){
this.width = width;
if(this.beSquare)
this.squareRule(width);
}
public void squareRule(double size){
this.height = size;
this.width = size;
}
public double getPerimeter() {
return 2*width + 2*height;
}
public double getArea() {
return width * height;
}
}
September 14th, 2009 at 5:16 pm
The solution to this problem is to throw IllegalArgumentException whenever a width has been specified that does not equal a height that has already been specified, or vice versa. Also add a method “setWidthAndHeight” that takes a single parameter.
September 14th, 2009 at 5:31 pm
UnconventionalCoder: What about a Rectangle that gets the same width and height? IllegalArgumentException too? 🙂
September 14th, 2009 at 6:29 pm
When Square setters are invoked it should be able to transform itself into rectangle. It’s possible to change type of runtime object in some languages. Obviously not in statically typed.
Mutable square is a mutable rectangle. It just can stop being mutable square after setter invocation. If you don’t want to model this operation all workarounds you proposed can be suitable depending on situation.
September 14th, 2009 at 7:29 pm
A while back I messed around with the idea of letting the type/class of the object being a mutable property (CHANGE-CLASS) in itself: http://paste.lisp.org/display/76971
Note that the identity of the object/instance remains the same all the time. So to check what shape we really have we just do a TYPE-OF or method dispatch as done in the tests in the paste or use TYPECASE or whatever.
(..i posted this comment in a reddit thread, but thought it might be interesting here too..)
September 15th, 2009 at 6:59 am
Am I alone in looking at this and saying Rectangle is a subclass of Square? Rectangle “adds” to Square the methods setWidth and SetHeight and getWidth and getHeight. The superclass Square only has setSide (which sets both sides the same) and getSide which can be overridden for Rectangle to require an extra parameter to specify whether to return Width or Height.
September 15th, 2009 at 7:35 am
The basic problem here is that the subclass adds a constraint on its base class instead of extending it. General OO languages are in my experience not suited for this.
I agree with what Dave Hemming says: extend the Square into an Rectangle. , This is better suited for Java and other OO languages with an equal mindset; This way the subclass extends the base class instead of restricting it.
September 15th, 2009 at 7:40 am
That’s a pretty far out idea Dave. A Rectangle is not a Square. Why should it inherit the
Square type? That’s like a car inheriting from motorcycle because it’s the same thing, only two more wheels. Or a more exact analogy: A Primate inherits Human because there’s more hair?
Mathematically a rectangle is a superset of possible squares. This is what leads people to subclass a square from a rectangle, but as many has pointed out, this might not be a smart thing to do since class inheritance is a different animal.
If the classes in question were more abstract, I could possibly agree with you. You have a class X with one field and a class Y with another and they have a few of the same derivable properties, sure. But if you are trying to model something with real world or mathematical analogs, it’s probably better to follow the real world’s example as you’ll end up with something more intuitive.
All programmers learn to avoid typing the same pieces of code twice, but using inheritance to do this when it is not “natural” for one class to inherit the other will take you to nasty places.
September 15th, 2009 at 7:57 am
To me, it’s a warning sign when my class hierarchies begin imitating real-world taxonomies; even this ultra-simple case seems to be causing headaches! My rule of thumb for a simple case such as this is 1) Extract a commom superclass that aids the implementation of four-sided things (or something or other in that abstract vein), and 2) Create Square and Rectangle as two subclasses of FourSidedThing (or whatever you called it).
I find that subclassing is a good restricted-scope implementation technique. It mostly causes problems when modelling. I like a class hierarchy that looks like a somewhat balanced tree, and has concrete classes only on the leaves. In short, I prefer to use subclassing to implement, not so much to model.
Oh, and 3) I bet your squares and rectangles are value objects, not entities (in the DDD jargon). Make the instances immutable.
September 15th, 2009 at 12:43 pm
Is it really necessary to have a separate type for ‘Square’? Can’t we just have a flag ‘isSquare’ in the Rectangle class and be done with it? This kinda seems a more practical solution; using a Rectangle with same width and height where a Square is desired doesn’t seem bad IMO.
September 15th, 2009 at 3:49 pm
A square has a different behavior than a rectangle. Increase the width or length of a square, you increase both dims equally. You don’t want a rectangle to do that, but some may want to know if it’s a square. Without an internal flag and a lot of assumptions about order of behavior, you can’t get a rectangle to assume the behaviors of a square, just because it meets the definition. You still want the rectangle behavior.
September 15th, 2009 at 8:12 pm
You don’t need to get all philosophical, just use private inheritance (C++)
class Square : private Rectangle
September 15th, 2009 at 9:43 pm
Bram had a different opinion.
I think Dave Hemming is on the right track, if squares and rectangles are to be salvaged at all. Inheritance does not have to represent “is-a”.
But for simple stuff like this ultimately I have to agree with Peter B that these should just be values, which don’t have methods (although there may be functions and operators on them) so they’re not so wedded to an exact interface. Java has done a disservice by teaching us that everything is an object.
September 16th, 2009 at 7:51 am
Surely, in the first (unsatisfying) fix, you mean the following?
public class Square extends Rectangle {
public void setWidth(double width) {
super.setWidth(width);
super.setHeight(width);
}
public void setHeight(double height) {
super.setWidth(height);
super.setHeight(height);
}
public void setSide(double size) {
super.setWidth(size);
super.setHeight(size);
}
}
September 16th, 2009 at 9:42 pm
C’mon guys, we really have to debug some of the sample code? I’m sure you all get the idea no?
I don’t see a problem with overriding the setHeight() and setWidth() rectangle arguments to make a square, if you wanted to make such a type. The immutable solution is nice, but limiting.
September 17th, 2009 at 8:43 am
A lot of interesting ideas and remarks above…
mathematically speaking:
– a rectangle is a (convex) polygon with 4 orthogonal edges
– a square adds another constraint: all sides same length
– both are immutable
– a geometrical transformation transforms each point one-by-one
– is is known, what transformations results are what kind of a shape
But:
– you will not resize a shape in mathematics, by resizing its length but by resizing its points, for rectangles the vertexes and redraw the edges
– setting the height or width of a rectangle is not a simple operation, it is a transformation on an infinite number of points
So:
– you simplified the mathematical rectangle to something named the same way, but have implemented the transformations wrong. Setting the height on a square will make it a different polygon, it will be a common rectangle.
– setting height, than width on a rectangle simply is a set of consecutive operations/transformations, which do not preserve the shape, but happen to result in the same shape after doing both with carefully chosen new values.
– what about choosing stretchHeight as the name for setHeight?
September 17th, 2009 at 9:02 am
… Still, when I first encountered this analogy as a thought exercise, I could not spot the failure lightly. It is the model that is violated, by adding the convenience methods setHeight and setWidth. It is an implementation detail and the contract is not declared. We just think, we know, what setHeight does. The comments regarding “resizing the shape” and “immutable, only constructor” are half way to finding the problem.
SetHeight seems correct on a rectangle, but we get a completely different object – it just happens to remain a rectangle, that is the reason we believe to be safe, but
1. It violates the encapsulation
2. and the immutability – changing the dimensions really replaces the old object with something new, that is were immutability is the safe way to go.
3. setHeight is a naive name, better use reshapeHeight. mySquare.reshapeHeight will result naturally in a myRectangle, which can be reshaped again by myRectangle.reshapeWidth and
m2ndSquare = Square.isSquare(myRectangle) ? new Square(myRectangle) : null;
September 18th, 2009 at 2:21 am
Then how about the following. In lin of Dave and Johann’s remarks:
How about calling the method ‘scale’ ?
You take the class Square and merely give it the method scale.
The class Rectangle then extends Square and adds the operations scalewidth and scaleHeight.
September 18th, 2009 at 2:39 am
Mmh… as I kept thinking an other idea occurred. What I just posted above this post is useless 🙁
I definitely have to go with Johann’s train of thought.
This would result in the following :
* all classes immutable. You cannot modify an object, merely create a new one.
*class Rectangle with methods
constructor: Rectangle( width, height)
Rectangle scale( percentage );
Rectangle reshapeWidth( width )
Rectangle reshapeHeigth( height)
*class Square extends Rectangle with methods:
constructor: Square(width)
constructor: Square( Rectangle )
Square scale( percentage );
Rectangle reshapeWidth( width)
Rectangle reshapeHeight( height )
boolean isSquare( Rectangle )
-if you want to create a square with a new specific height, use the constructor.
-If you modify the width/height of an existing Square or Rectangle a new Rectangle object will be the result.
-When you scale a Square a new Square will result. When you scale a Rectangle a new Rectangle will result.
September 18th, 2009 at 4:59 pm
Fascinating discussion.
Our intuition about rectangles and squares is as immutable objects. If you try to model this, then indeed ImmutableSquare is a subclass of ImmutableRectangle and it should use constructors. If you want mutation, then a Square is a 1-parameter shape and a rectangle is a 2-parameter shape. These are clearly not the same. Here’s a way:
public abstract class MutableShape;
private Integer numberOfParameters;
private double[] parameters;
MutableShape(Integer numberOfParameters, double… parameters);
if (numberOfParameters >= 0) {
this.numberOfParameters = numberOfParameters;
setParameters(parameters);
}
else throw new IllegalArgumentException(“numberOfParameters must be non-negative”);
}
void setParameters(double… parameters) {
if (parameters.length == numberOfParameters)
this.parameters = parameters;
else throw new IllegalArgumentException(“wrong number of parameters”);
}
public double[] getParameters() { return parameters; }
public abstract double getArea();
}
—————————-
public class Square extends MutableShape {
public Square(double size) {
super(1, size);
}
public void setSize(double size) { getParameters()[0] = size; }
public double getSize() { return getParameters()[0]; }
public double getArea() {
double size = getSize();
return size * size;
}
}
—————————-
public class Rectangle extends MutableShape {
public Rectangle(double height, double width) {
super(2, height, width);
}
public double getHeight() { return getParameters()[0]; }
public double getWidth() { return getParameters()[1]; }
public void setHeight(double height) { getParameters()[0] = height; }
public void setWidth(double width) { getParameters()[1] = width; }
public double getArea() {
return getHeight() * getWidth();
}
public boolean isSquare() { return getHeight() == getWidth(); }
public Square toSquare() {
return isSquare() ? new Square(getHeight()) : null;
}
September 18th, 2009 at 6:36 pm
> Dave Hemming Says:
>
> Am I alone in looking at this and saying Rectangle is a subclass of Square?
Not even in your dreams 🙂
——————
Minor nit:
If
-> Rectangle extends Square with setHeight, setWidth
then
-> Shape extends Square, Rectangle, Circle, … with setSize, setHeight, setWidth, setRadius, …
——————
Major nit:
class Square …
class Rectangle extends Square…
Square sq = new Rectangle(10, 5);
oops….
——————
Really, this thread leads to the conclusion that for IS-A what we really want is:
class Rectangle…
class Square restricts Rectangle…
😉
September 22nd, 2009 at 4:26 am
http://www.reddit.com/r/programming/comments/9mvq1/a_square_is_a_rectangle/
October 1st, 2009 at 11:13 am
I used to use this as an interview question. I’d ask a candidate whether a square was a subclass of a rectangle, or a rectangle a subclass of a square. Whichever answer they gave, I’d point out the problems. A good candidate will show an ability to reason about this stuff. (There is, of course, no “right” answer – but all kinds of answers – like “in class, we were told that squares were subclasses of rectangles” – are wrong.)
Although people try to use mathematical arguments about types, types play little role in mathematics. Neither “Square” nor “Rectangle” would be thought of as a type. There is a set of all Rectangles, which has a subset of all Squares. Because Square is a subset, any function whose *domain* is Rectangle is defined for all members of Square. On the other hand, there’s no particularly interesting connection between functions whose *range* is Rectangle and functions whose *range* is Square. It may be interesting to prove that some function described as having a range of Rectangle actually only produces Squares, but that’s proving something about the function, not talking about types. Mutability isn’t strictly an issue – in using English, a mathematician will certainly say things like “doubling the length of the sides of a Square multiplies the area by 4”, which *sounds* like the square is being modified, even to a mathematician. Formally, though, if you pushed, we can describe the same thing functionally – we have a function from Square to Square that gives you a new square (this is itself just an informal phrase!). A function that only doubled the horizontal dimension would go from Square to Rectangle. No big deal. You can recast your classes to be immutable, but as long as you have functions that return new, modified instances, you have the same problems.
LSP is a fine idea, but it only goes so far. If I have a “typeof” operator that returns the dynamic type, no class is really substitutable for another, because they produce different results from “typeof”. Does that bother you? The whole notion of “isa” as the defining relationship for subclasses/subtypes fails for the same reason.
Programming isn’t mathematics. Subclassing and even subtyping are programming concepts, not mathematical concepts, though there’s a fine mathematical theory of types developed exactly to help you understand the implications of choosing a style of subclassing/subtyping/genericity/etc. If you’re concerned about displaying geometric objects – where this whole discussion usually arises – then thinking of a Square as a “kind of” Rectangle which is a “kind of” Polygon which is a “kind of” Shape is useful. Once you begin thinking about what kinds of mutators are appropriate, things get much more interesting. Consider a graphical editor: If I create a Square, then grab it and resize it, do I expect it to stay square, or can I stretch it one way and make it a Rectangle? Now you’re talking neither mathematics nor programming – you’re talking UI design and user expectations.
— Jerry
November 26th, 2009 at 3:22 am
bwtaylor has it right. You need to separate the actual information from the Rectangle api.
I would perhaps eg. define protected abstract class Dims in Rectangle, which has intance variables width and height and setters for them. Then Rectangle has inner private class that extends/imlements that class. Rectangle has also setters and getters for width and height, where dimensions is manipulated through Dims instance.
Now, Square inherits Rectangle and has inner private class that inherits Dims and overrides setters, where width and height is set the same.
So what is really changed in Square, is just one implementation of abstract class, that is not exposured by Rectangle-api.
That was not very good explanation, sorry, but my example works:
public class Rectangle {
protected Dims dims;
public Rectangle() {
dims = new RecDims(0, 0);
}
public Rectangle(int width, int height) {
dims = new RecDims(width, height);
}
public int getWidth() { return dims.width; }
public int getHeight() { return dims.height; }
public void setWidth(int wid) { dims.setWidth(wid); }
public void setHeight(int hei) { dims.setHeight(hei); }
private class RecDims extends Dims {
public RecDims(int wid, int hei) {
width = wid;
height = hei;
}
}
protected abstract class Dims {
public int width;
public int height;
public void setWidth(int wid) { width = wid; }
public void setHeight(int hei) { height = hei; }
}
}
public class Square extends Rectangle {
public Square(int dim) {
dims = new SquDims(dim);
}
private class SquDims extends Dims {
public SquDims(int dim) {
width = dim;
height = dim;
}
public void setWidth(int wid) { width = wid; height = wid; }
public void setHeight(int hei) { height = hei; width = hei; }
}
}
August 1st, 2010 at 3:25 am
[…] should be based on most efficient code reuse, not the domain, because as anybody who has heard the square is not a rectangle* example can attest, sometimes the domain ‘is-a’ relationship just doesn’t […]
March 5th, 2013 at 8:37 am
[…] Cafes Programming presents a compelling argument for why a Square Is Not a Rectangle. Our Square class does not violate the so-called contract of the Rectangle class (the list of […]
May 10th, 2013 at 3:40 pm
At Ville, having dims be protected would prevent you from accessing it from another package,
wouldn’t it make more sense to
have an abstract base class (at the top of the heirarchy) called “Shape” with abstract methods getArea and getCircumference.
derive an immutable rectangle called “Rectangle” from “Shape” which has public getWidth() and getHeight() methods
derive a “MRectangle” mutable rectangle from “Rectangle” that has public setWidth() and setHeight() methods
derive an immutable square called “Square” from Rectangle whose constructor takes a single argument
and derive a mutable square called “MSquare” from “Square” with a public method setSide()
thus a mutable square is a square is a rectangle but a square is not a mutable rectangle
if you wanted to go further up the tree you could have imutable quadrilaterals derived from Shape, immutable parallelograms derive from quadrilaterals, immutable rectangles derive from parallelograms, immutable rhombus-es derive from parallelograms, and then you want multiple inheritance in C++ so the immutable Square can derive from both rhombus-es and rectangles, and you would want M (for mutable) versions deriving off of all the immutable types
August 7th, 2013 at 5:31 pm
[…] very interesting example that explains LSP is “why square is not a rectangle?“. Suppose, you have a Rectangle class with members height and width and public […]
August 28th, 2015 at 1:32 am
[…] bad use of inheritance is the canonical rectangle and square example in Java that Rusty Harold examines. However, it is not inheritance per se but the inherited getters and setters that are the real […]
November 9th, 2015 at 6:31 am
[…] TB [2:00 PM] http://cafe.elharo.com/programming/a-square-is-not-a-rectangle/ […]
March 15th, 2017 at 5:50 am
This is a interesting idea/problem, and after thinking about it a bit i came the following, please correct me if im making a mistake:
I dont think in development there are a lot of good reasons to ever have a square. Most likely you will always (functionaly) want a rectangle, if you need to have it equalsided this should, in my eyes, be a either a option for your rectangle or somethinga outside class determines.
Like Jerry Leichter said above. What would happen if we place a square and move a single cornor up. Will it really behave like a square? So we will need to remove the square and insert a rectangle? Or are we going to dynamicaly change the square to a rectangle?
Unlikely, probally we will have a rectangle with a little lock icon/option to lock sides.
Also if we need, for instance, to always create rectrangles that have double the height compared to the width. Would we again inherit from rectangle and make a DoubleheightRectangle?
I would either implement it like below or, if i have multiple options, create a ‘ExtendedRectangle extends Rectangle’ that does this, and more, for me. Or if i was working with viewmodels, i would most perhaps limit the side-lock inside a RectangleViewModel, based on a usersetting.
public class Rectangle {
private height: int;
private width: int;
//should perhaps be mutually exclusive
private rectangleOptions = {
equalSides: boolean = false;
doubleHeight: boolean = false;
}
//would need to account for the different options
resize(height, width) {
this.width = (this.rectangleOptions.equalSides ? (width > height ? width : height) : width;
this.height = (this.rectangleOptions.equalSides ? (height > width ? height : width) : height;
}
public height {
get: {
return this.height;
}
}
public width {
get: {
return this.width
}
}
}