Marco

Liskov Substitution Principle (LSP) states that every super type T can be replaced with one of its sub-types S without affecting the correctness of the program. In other words, sub-types of a super type should not alter the super type default behavior.

To explain this more; let’s have a look at the following code that violates LSP:

public class Rectangle {

    protected int width;

    protected int height;

    public int getWidth() {

        return width;

    }

    public void setWidth(int width) {

        this.width = width;

    }

    public int getHeight() {

        return height;

    }

    public void setHeight(int height) {

        this.height = height;

    }

    public int calculateArea() {

        return width * height;

    }

}

public class Square extends Rectangle {

    public void setHeight(int height) {

        this.height = height;

        this.width = height;

    }

    public void setWidth(int width) {

        this.height = width;

        this.width = width;

    }

}

Note: The Square class is altering its parent class default behavior by overriding both of its setWidth and setHeight methods; and that’s to always make sure that both of the Square width and height are equal!

Now, we can create a new Rectangle object and set its width and height, then calculate its area, and we can do the same with the Square.

 

From the first sight, everything looks ok; but actually it is not! Let’s see the following code:

// Test 1

Rectangle r1 = new Rectangle();

r1.setHeight(2);

r1.setWidth(3);

System.out.println("R1 area is: " + r1.calculateArea()); // R1 area is: 6

// Test 2

Rectangle r2 = new Square();

r2.setHeight(2);

r2.setWidth(3);

System.out.println("R2 area is: " + r2.calculateArea()); // R2 area is: 9

The second test shows that the above code violates Liskov Substitution Principle, because when we tried to substitute the Rectangle object r2 with a Square sub-type, it produced undesired area output of 9.

Ok then, knowing that the Square is an equal sided Rectangle, what we’re going to do to model this correctly without violating LSP!

The key here is to modify the super type (base class) a little bit, by removing both of the width and height setter methods, and provide both of their values when instantiating (constructing) a new Rectangle object. See the following code:

public class Rectangle {

    private int width;

    private int height;

    public Rectangle(int width, int height)

    {

        this.width = width;

        this.height = height;

    }

    public int getWidth() {

        return width;

    }

    public int getHeight() {

        return height;

    }

    public int calculateArea() {

        return width * height;

    }

}

public class Square extends Rectangle {

    public Square(int width)

    {

        super(width,width);

    }   

}

 

// Test 1

Rectangle r1 = new Rectangle(2,3);

System.out.println("R1 area is: " + r1.calculateArea()); // R1 area is: 6

// Test 2

Rectangle r2 = new Square(2);

System.out.println("R2 area is: " + r2.calculateArea()); // R2 area is: 4

Now, with this simple modification, the area of the Rectangle in both tests is calculated correctly, whether the actual object was a Rectangle or a Square, and without violating Liskov Substitution Principle.

Leave a Reply

Your email address will not be published.

Captcha Time limit is exhausted. Please reload the CAPTCHA.