Принцип подстановки Лисков (Liskov Substitution Principle, LSP) утверждает, что объекты подклассов должны быть взаимозаменяемы с объектами базового класса без изменения поведения программы. Это означает, что подклассы не должны нарушать контракт базового класса или изменять его логику. LSP является третьим принципом в наборе SOLID и играет ключевую роль в создании устойчивой и понятной иерархии классов.

Чтобы соответствовать LSP:

  • Подклассы должны расширять функциональность базового класса, не изменяя его поведение.
  • Контракты, задаваемые базовыми классами, должны строго соблюдаться.
  • Следует избегать переопределения методов, если это изменяет их ожидаемое поведение.

Преимущества соблюдения LSP:

  1. Предсказуемость: Код, использующий базовый класс, будет работать одинаково независимо от того, какие подклассы используются.
  2. Упрощение тестирования: Система становится менее подверженной ошибкам, так как базовый контракт всегда соблюдается.
  3. Гибкость и масштабируемость: Добавление новых подклассов не требует модификации существующего кода, если соблюден принцип LSP.
  4. Улучшенная читаемость: Четкое разделение обязанностей между базовыми и дочерними классами упрощает понимание системы.

Пример нарушения LSP

Рассмотрим иерархию классов для птиц:

public class Bird {
    public void fly() {
        // Логика полета
        System.out.println("I can fly!");
    }
}
 
public class Penguin extends Bird {
    @Override
    public void fly() {
        // Пингвин не может летать — нарушение LSP
        throw new UnsupportedOperationException("Penguins cannot fly");
    }
}
 

В данном случае класс Penguin нарушает контракт базового класса Bird. Код, который ожидает, что любой объект типа Bird может летать, перестанет работать корректно при использовании Penguin. Это ведет к непредсказуемому поведению программы и увеличивает сложность сопровождения.

Для устранения нарушения следует пересмотреть иерархию классов, чтобы явно выделить летающих и нелетающих птиц:

public abstract class Bird {
    // Общие свойства и методы для всех птиц
}
 
public interface Flyable {
    void fly();
}
 
public class FlyingBird extends Bird implements Flyable {
    @Override
    public void fly() {
        // Реализация полета
        System.out.println("I can fly!");
    }
}
 
public class Penguin extends Bird {
    // Пингвин остается нелетающей птицей
}
 

Теперь поведение каждой птицы становится очевидным, и программа корректно работает с летающими и нелетающими птицами, не нарушая LSP.


Мета информация

Область:: 00 Архитектура ПО
Родитель:: SOLID
Источник::
Создана:: 2024-09-27
Автор::

Дополнительные материалы

Дочерние заметки