LSP

Принцип подстановки Барбары Лисков | Liskov Substitution Principle | LSP

Наследующий класс должен дополнять (extend — расширять), а не изменять базовый.

Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом.

Tип S будет подтипом Т тогда и только тогда, когда каждому объекту oS типа S соответствует некий объект objectT типа T таким образом, что для всех программ P, реализованных в терминах T, поведение P не будет меняться, если objectT заменить на oS.

// ExpensiveCar extends Car
// 1. Написали программу с классом Car
// 2. Заменили Car на ExpensiveCar
// 3. Ни один метод не сломался? Ура! LSP соблюден!!! 
// Подтипы должны быть заменяемы их исходными типами.

let car = new Car();

car.drive();
car.stop();

car = new ExpensiveCar();

car.drive();
car.stop();

Более простыми словами можно сказать, что поведение наследуемых классов не должно противоречить поведению, заданному базовым классом, то есть поведение наследуемых классов должно быть ожидаемым для кода, использующего переменную базового типа.

// Представим, что мы имеем объект прямоугольник

class Rectangle {
    setWidth (val) {
        this.width = val;
    }

    setHeight (val) {
        this.height = val;
    }

    getArea () {
        this.width * this.height;
    }
}

/*
* Позже нам понадобился объект квадрат.
* Основываясь на том, что квадрат является частным случаем прямоугольника,
* стороны которого равны, мы решили создать объект квадрат
* и использовать его вместо прямоугольника.
* */

class Square extends Rectangle {
    setSize (val) {
        this.size = val;
    }

    getArea () {
        this.size * this.size;
    }
}

/*
 Проблема в том, что объект квадрат не может использоваться
 в любом коде вместо прямоугольника.
 */

let figure = new Rectangle();

figure.setHeight(2);
figure.setWidth(3);
figure.getArea(); // 6

figure = new Square();

figure.setHeight(2);
figure.setWidth(figure.height);
figure.getArea(); // undefined (suddenly!)

Изначально LSP рассматривался как руководство по использованию наследования в ООП. Но со временем принцип приобрел более широкое применение - как принцип проектирования программных систем. Он стал распространяться на интерфейсы и реализации.

Существуют пользователи, которые зависят от четкого определения интерфейсов и взаимозаменяемости реализаций этих интерфейсов.

Last updated