Принцип замещения Лискова — это третий принцип SOLID для написания чистого кода. Если вы хотите прочитать с самого начала, начните здесь.

Принцип замещения Лисков гласит, что объекты суперкласса должны быть заменяемы объектами его подклассов без нарушения работы приложения. Итак, что это значит?

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

class Bird {
  fly() {
    console.log("I am flying");
  }
}

class Duck extends Bird {
  quack() {
    console.log("I am quacking");
  }
}

class Penguin extends Bird {
  fly() {
    throw new Error("I cannot fly");   // As Penguin cannot fly.
  }

  swim() {
    console.log("I am swimming");
  }
}

function makeBirdFly(bird) {
  bird.fly();
}

const bird = new Bird();
const duck = new Duck();
const penguin = new Penguin();

makeBirdFly(bird);
makeBirdFly(duck);
makeBirdFly(penguin); // This will throw error as penguin's cannot fly.

Как написано в приведенном выше коде, у класса Bird есть метод fly, так как птицы могут летать. Duck является подклассом Bird, поэтому у него тоже есть метод fly. Кроме того, у него есть шарлатанский метод издавать звук. У нас также есть класс Penguin, но поскольку пингвины не могут летать, мы должны переопределить метод и выдать ошибку. Поскольку пингвины умеют плавать, у нас есть способ сделать это.

Затем мы создали объекты всех 3-х классов и вызвали на них метод fly. Это выдает ошибку в случае класса пингвинов, потому что даже если пингвин — птица, он не может летать.

Это нарушает принцип подстановки Лискова, потому что Penguin является подклассом Bird, но все же мы не можем заменить объекты класса Bird классом Penguin, поскольку объекты класса Penguin вызовут ошибку для метода fly.

Чтобы решить эту проблему, мы можем создать 2 отдельных класса, один для летающих птиц, а другой для плавающих птиц, как показано ниже:

class FlyingBird {
  fly() {
    console.log("I am flying");
  }
}

class SwimmingBird {
  swim() {
    console.log("I am swimming");
  }
}

class Duck extends FlyingBird {
  quack() {
    console.log("I am quacking");
  }
}

class Penguin extends SwimmingBird {
}

function makeFlyingBirdFly(bird) {
  bird.fly();
}

function makeSwimmingBirdSwim(bird) {
  bird.swim();
}

const duck = new Duck();
const penguin = new Penguin();

makeFlyingBirdFly(duck); // I am flying
makeSwimmingBirdSwim(penguin); // I am swimming

В приведенном выше фрагменте кода мы создали отдельные классы для летающих и плавающих птиц. Класс Duck теперь наследуется от FlyingBird, что дает ему возможность летать, а класс Penguin наследуется от Swimbird, который дает ему возможность плавать. Здесь соблюдается принцип замещения Лискова, потому что вы можете заменить объекты родительского класса дочерним классом, и все будет работать нормально, поскольку дочерние классы не переопределяют функциональные возможности родительских классов.

Надеюсь, вы получили некоторое представление о принципе замещения Лискова. Если у вас есть какие-либо предложения, не стесняйтесь добавлять их в комментариях ниже. Пожалуйста, хлопайте, если вам понравился материал. Вы можете прочитать больше моих блогов на моем сайте https://blog.csschool.io/