Маленькие заметки для тех, кому сложно понять кучу умных слов,
Single Responsibility Principle (SRP) — принцип единственной ответственности
Стих:
Смысл единственной ответственности
Делай модули поменьше, плоди наследственности.
Обращение к базе данных, генерация html,
Запихнул ты в суперкласс — ты очень неумел!
Объяснение:
Каждый класс должен решать только одну задачу. Если класс занимается логированием, работой с базой данных и рендерингом HTML — это нарушение SRP.
// Плохой пример: класс делает всё class User { public function saveToDatabase(): void { /* ... */ } public function generateHtmlReport(): string { /* ... */ } } // Хороший пример: разделение ответственности class User { public function getData(): array { /* ... */ } } class UserRepository { public function save(User $user): void { /* ... */ } } class UserHtmlRenderer { public function render(User $user): string { /* ... */ } }
Open Closed Principle (OCP) — принцип открытости-закрытости
Стих:
Открыт для расширения, изменениям закрыт,
Этот принцип сильно, в описании размыт.
Не надо нам условий, в родителе плодить,
Добавь ему наследников, и им теперь рулить.
Объяснение:
Классы должны быть открыты для расширения (через наследование, композицию), но закрыты для модификации.
// Плохой пример: добавление новых типов требует изменения класса class Logger { public function log(string $type, string $message): void { if ($type === 'file') { // запись в файл } elseif ($type === 'database') { // запись в базу } } } // Хороший пример: использование абстракций interface Logger { public function log(string $message): void; } class FileLogger implements Logger { public function log(string $message): void { /* ... */ } } class DatabaseLogger implements Logger { public function log(string $message): void { /* ... */ } } class LogProcessor { public function __construct(private Logger $logger) {} public function process(string $message): void { $this->logger->log($message); } }
Liskov Substitution Principle (LSP) — принцип подстановки Лисков
Стих:
Любимый принцип подстановки, Барбары Лисков,
Он на самом деле, в подходе прям суров!
Если есть наследник, то должен он уметь,
Все что и родитель, ничто не затереть.
Объяснение:
Наследники должны сохранять поведение родительского класса. Если класс Dog наследуется от Animal, то везде, где используется Animal, можно использовать Dog без сбоев.
// Нарушение LSP: класс Square меняет поведение родителя class Rectangle { public function setWidth(int $w): void { /* ... */ } public function setHeight(int $h): void { /* ... */ } } class Square extends Rectangle { public function setWidth(int $w): void { $this->width = $w; $this->height = $w; // Неожиданное поведение! } } // Решение: не наследовать Square от Rectangle, если их поведение разное.
Interface Segregation Principle (ISP) — принцип разделения интерфейса
Стих:
Интерфейсики огромные, нам следует разбить,
Лучше их по коду, побольше наплодить!
Захочешь почесаться, чихнуть или зевнуть,
Отдельный интерфейс написать ты не забудь!
Объяснение:
Интерфейсы должны быть узкоспециализированными. Клиенты не должны зависеть от методов, которые они не используют.
// Плохой пример: "толстый" интерфейс interface Worker { public function work(): void; public function sleep(): void; } class HumanWorker implements Worker { public function work(): void { /* ... */ } public function sleep(): void { /* ... */ } } class RobotWorker implements Worker { public function work(): void { /* ... */ } public function sleep(): void { throw new Exception('Роботы не спят!'); // Нарушение ISP } } // Хороший пример: разделение интерфейсов interface Workable { public function work(): void; } interface Sleepable { public function sleep(): void; } class HumanWorker implements Workable, Sleepable { /* ... */ } class RobotWorker implements Workable { /* ... */ }
Dependency Inversion Principle (DIP) — принцип инверсии зависимостей
Стих:
Депенденси инвершн, проще говоря,
Используйте абстракции, как понимаю я!
Передавайте в методы, какой-то внешний класс,
А в нем храним мы логику, и клево все у нас!
Объяснение:
Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
// Плохой пример: зависимость от конкретной реализации class PasswordReminder { private MySQLConnection $db; public function __construct() { $this->db = new MySQLConnection(); // Жёсткая связь } } // Хороший пример: зависимость от абстракции interface Connection { public function connect(): void; } class MySQLConnection implements Connection { public function connect(): void { /* ... */ } } class PasswordReminder { public function __construct(private Connection $db) {} }
Интересные факты о SOLID
Акроним придумал Роберт Мартин. Термин SOLID впервые появился в его статье 2000 года, хотя сами принципы разрабатывались разными авторами (например, Барбарой Лисков).
LSP — часть теории подтипов. Принцип Лисков формализован в компьютерной науке как «контрактное проектирование».
DIP — основа современных фреймворков. Инверсия зависимостей лежит в основе IoC-контейнеров (например, в Laravel).
SRP vs. God Object. Нарушение SRP часто приводит к созданию «божественных классов», которые сложно тестировать и поддерживать.
OCP и паттерн Стратегия. Принцип открытости/закрытости часто реализуется через стратегию, декоратор или фабричный метод.
Итог: SOLID — не догма, а инструмент. Используйте его там, где он уменьшает сложность, а не создаёт её.
За пояснениями сюда:
