Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's wrong with the Square and Rectangle inheritance?

I've read some of article about the practice that making Square an inheritance class of Rectangle class is a bad practice, saying it violate the LSP (Liskov substitution principle). I still don't get it, I made a example code in Ruby:

class Rectangle
    attr_accessor :width, :height
    def initialize(width, height)
        @width = width
        @height = height
    end
end

class Square < Rectangle
    def initialize(length)
        super(length, length)
    end
    def width=(number)
        super(number)
        @height = number
    end

    def height=(number)
        super(number)
        @width = number
    end
end


s = Square.new(100)

s.width = 50

puts s.height

Could anyone tell me what's wrong with it?

like image 381
mko Avatar asked May 29 '13 03:05

mko


People also ask

Should square inherit rectangle?

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.

Should square be subclass of rectangle?

Explanation: We know that a square really is just a subclass of a rectangle, for it is merely a rectangle having four sides that are all equal.

Is a square considered a rectangle?

A rectangle is a quadrilateral, all of whose angles are equal i.e. right angles. Square, apart from all equal angles, also has all sides equal. Hence, square is a special case of rectangle. In other words rectangle is sometimes a square (when all sides too are equal).

Is a square a rectangle UK?

A square is a rectangle by definition. To figure this out, you have to take a look at the definitions of each shape. A square is a shape that has four equal length sides and four 90 degree right angles. A rectangle is a shape with four straight sides and four right angles.


1 Answers

I'm not always keen on Liskov since it seems to limit what you can do with inheritance based on behaviour rather than "essence". In my view, inheritance was always meant to be an "is a" relationship, not an "acts exactly like" one.

Having said that, the wikipedia article goes into detail as to why this is considered bad by some, using your exact example:

A typical example that violates LSP is a Square class that derives from a Rectangle class, assuming getter and setter methods exist for both width and height.

The Square class always assumes that the width is equal with the height. If a Square object is used in a context where a Rectangle is expected, unexpected behavior may occur because the dimensions of a Square cannot (or rather should not) be modified independently.

This problem cannot be easily fixed: if we can modify the setter methods in the Square class so that they preserve the Square invariant (i.e., keep the dimensions equal), then these methods will weaken (violate) the postconditions for the Rectangle setters, which state that dimensions can be modified independently.

So, looking at your code alongside the equivalent Rectangle code:

s = Square.new(100)            r = Rectangle.new(100,100)
s.width = 50                   r.width = 50
puts s.height                  puts r.height

the output would be 50 on the left and 100 on the right.

But, this is the important bit from the article, in my view:

Violations of LSP, like this one, may or may not be a problem in practice, depending on the postconditions or invariants that are actually expected by the code that uses classes violating LSP.

In other words, provided the code using the classes understands the behaviour, there is no issue.

Bottom line, a square is a proper subset of a rectangle, for a loose-enough definition of rectangle :-)

like image 162
paxdiablo Avatar answered Oct 14 '22 04:10

paxdiablo