Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
LSP (Liskov Substitution Principle) — Принцип подстановки Барбары Лисков
LSP — один из пяти принципов SOLID, который гласит: объекты подклассов должны корректно заменять объекты базовых классов без нарушения корректности программы. Проще говоря, если класс B наследует от класса A, то везде, где используется A, можно безопасно подставить B.
Суть принципа
// ❌ Нарушение LSP
abstract class Bird {
abstract public function fly();
}
class Sparrow extends Bird {
public function fly() {
return "Sparrow flies";
}
}
class Penguin extends Bird {
public function fly() {
throw new Exception("Penguin cannot fly!");
}
}
// Проблема: код, работающий с Bird, сломается на Penguin
function makeBirdFly(Bird $bird) {
return $bird->fly(); // Взлетит исключение для пингвина!
}
Правильное решение:
// ✅ Соблюдение LSP
interface Bird {
public function move();
}
class FlyingBird implements Bird {
public function move() {
return "Flying";
}
}
class SwimmingBird implements Bird {
public function move() {
return "Swimming";
}
}
class Sparrow extends FlyingBird {}
class Penguin extends SwimmingBird {}
// Теперь все Bird могут быть подставлены безопасно
function makeBirdMove(Bird $bird) {
return $bird->move(); // Работает для всех!
}
Ключевые правила LSP
- Не усиливай предусловия — метод подкласса должен принимать не менее общие данные, чем у родителя
- Не ослабляй постусловия — метод подкласса должен возвращать не менее конкретный результат
- Не выбрасывай новые исключения — подкласс не должен выбрасывать исключения, которые родитель не выбрасывает
- Сохраняй инварианты — ограничения, истинные для родителя, остаются истинными для подкласса
Пример с коллекциями
// ❌ Нарушение LSP
class Stack extends Array {
public function push($value) {
// переопределение поведения
}
public function pop() {
// неожиданное поведение
}
}
// Если код ожидает Array, он сломается на Stack
// ✅ Правильно — использование интерфейсов
interface Collection {
public function add($item);
public function remove();
}
class Stack implements Collection {
private array $items = [];
public function add($item) {
$this->items[] = $item;
}
public function remove() {
return array_pop($this->items);
}
}
class Queue implements Collection {
private array $items = [];
public function add($item) {
$this->items[] = $item;
}
public function remove() {
return array_shift($this->items);
}
}
// Обе коллекции могут использоваться везде
function processCollection(Collection $c) {
$c->add("item");
return $c->remove();
}
Преимущества LSP
- Предсказуемость — код работает ожидаемо для всех подтипов
- Гибкость — легко добавлять новые реализации без изменения клиентского кода
- Надежность — меньше скрытых ошибок и неожиданного поведения
- Тестируемость — проще юнит-тестировать полиморфный код
LSP в реальном коде
// ✅ Хороший пример из реальных проектов
interface PaymentGateway {
public function charge(float $amount): bool;
}
class StripeGateway implements PaymentGateway {
public function charge(float $amount): bool {
// Stripe логика
return true;
}
}
class PayPalGateway implements PaymentGateway {
public function charge(float $amount): bool {
// PayPal логика
return true;
}
}
class PaymentService {
public function __construct(private PaymentGateway $gateway) {}
public function processPayment(float $amount): bool {
return $this->gateway->charge($amount);
}
}
// Можно подставить любой шлюз, код работает одинаково
$service = new PaymentService(new StripeGateway());
// или
$service = new PaymentService(new PayPalGateway());