3. Zasada podstawienia Liskov

Zasada podstawienia Liskov (Liskov Substitution Principle, LSP).

Mówi ona, że obiekt danej klasy powinien być zastępowalny obiektem dowolnej klasy dziedziczącej po niej, nie powodując błędów w programie.
Innymi słowy, klasa podrzędna powinna być w stanie zastąpić swoją klasę nadrzędną bez zmiany oczekiwanego zachowania programu.

Poniżej znajdziesz przykład zasady LSP w języku Java. Załóżmy, że mamy hierarchię klas reprezentujących prostokąty i kwadraty:
Java
// Klasa łamiąca zasadę LSP
class Rectangle {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int calculateArea() {
        return width * height;
    }
}

// Klasa dziedzicząca łamiąca zasadę LSP
class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width;
    }

    @Override
    public void setHeight(int height) {
        this.width = height;
        this.height = height;
    }
}

public class Main {
    public static void main(String[] args) {
        // Użycie klas łamiących zasadę LSP
        Rectangle rectangle = new Square();
        rectangle.setWidth(5);
        rectangle.setHeight(10);

        // Błąd: Oczekiwano, że prostokąt, ale jest to kwadrat
        int area = rectangle.calculateArea();
        System.out.println("Area: " + area);
    }
}

W powyższym przykładzie klasa Square dziedziczy po klasie Rectangle, ale narusza zasadę LSP poprzez nadpisywanie metod setWidth i setHeight. W efekcie obiekt typu Square, gdy jest używany zamiast obiektu typu Rectangle, prowadzi do nieprawidłowych wyników.

Aby naprawić to naruszenie, możemy ponownie zaprojektować hierarchię klas lub dostosować metody w sposób, który nie zmienia oczekiwanego zachowania dla klas podrzędnych.

Jednym z możliwych podejść jest wykorzystanie interfejsu, aby uniknąć problemów związanych z dziedziczeniem wielokrotnym:

Java
// Interfejs zgodny z zasadą LSP
interface Shape {
    void setWidth(int width);
    void setHeight(int height);
    int calculateArea();
}

class Rectangle implements Shape {
    protected int width;
    protected int height;

    @Override
    public void setWidth(int width) {
        this.width = width;
    }

    @Override
    public void setHeight(int height) {
        this.height = height;
    }

    @Override
    public int calculateArea() {
        return width * height;
    }
}

class Square implements Shape {
    private int side;

    @Override
    public void setWidth(int width) {
        this.side = width;
    }

    @Override
    public void setHeight(int height) {
        this.side = height;
    }

    @Override
    public int calculateArea() {
        return side * side;
    }
}

public class Main {
    public static void main(String[] args) {
        // Użycie interfejsu zgodnego z zasadą LSP
        Shape rectangle = new Rectangle();
        rectangle.setWidth(5);
        rectangle.setHeight(10);
        int rectangleArea = rectangle.calculateArea();
        System.out.println("Rectangle Area: " + rectangleArea);

        Shape square = new Square();
        square.setWidth(5);
        square.setHeight(5);
        int squareArea = square.calculateArea();
        System.out.println("Square Area: " + squareArea);
    }
}

W tym poprawionym przykładzie używamy interfejsu Shape, który jest implementowany zarówno przez Rectangle, jak i Square.

To pozwala na zastępowanie obiektów bez zmiany oczekiwanego zachowania programu, spełniając zasadę podstawienia Liskov.

Scroll to Top