Принципы SOLID в PHP
SOLID — это акроним пяти принципов проектирования, направленных на создание более понятного, гибкого и поддерживаемого программного обеспечения.
1. Принцип единственной ответственности (SRP)
Класс должен иметь одну и только одну причину для изменения.
Это означает, что у класса должна быть только одна задача. Например, если класс одновременно работает с данными пользователя и отправляет электронные письма, его следует разделить на два класса.
2. Принцип открытости/закрытости (OCP)
Программные сущности (классы, модули, функции и т.д.) должны быть открыты для расширения, но закрыты для модификации.
Вы должны иметь возможность добавлять новую функциональность, не изменяя существующий код. Обычно это достигается с помощью интерфейсов или абстрактных классов.
3. Принцип подстановки Лисков (LSP)
Пусть $\phi(x)$ — свойство, доказуемое для объектов $x$ типа $T$. Тогда $\phi(y)$ должно быть истинным для объектов $y$ типа $S$, где $S$ является подтипом $T$.
Простыми словами: дочерние классы должны иметь возможность заменять родительские классы без нарушения работы приложения.
4. Принцип разделения интерфейса (ISP)
Клиент не должен зависеть от методов, которые он не использует.
Лучше иметь несколько специализированных интерфейсов, чем один большой универсальный интерфейс.
5. Принцип инверсии зависимостей (DIP)
- Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Это означает, что следует зависеть от интерфейсов, а не от конкретных классов.
Каверзные вопросы по принципу подстановки Лисков (LSP)
- Нарушает ли LSP добавление публичного поля в наследуемый класс? Как правило, нет. LSP касается поведенческого подтипирования. Однако если базовый класс предполагается использовать так, что у него должны быть только определённые поля, добавление новых может усложнить его использование, но это не нарушает строгую подстановку базового класса.
- Нарушает ли LSP выброс исключения внутри метода наследуемого класса? Да, если метод базового класса не определяет, что может выбрасывать данное исключение (или любое исключение вообще). Вызывающий код ожидает поведение базового класса. Если дочерний класс выбрасывает неожиданное исключение, вызывающий код сломается, что нарушает LSP.
Пример:
Вместо:
class MySQLConnection {
public function connect() { ... }
}
class PasswordReminder {
private $db;
public function __construct(MySQLConnection $db) {
$this->db = $db;
}
}
Используйте (DIP):
interface DBConnectionInterface {
public function connect();
}
class PasswordReminder {
private $db;
public function __construct(DBConnectionInterface $db) {
$this->db = $db;
}
}
Теперь PasswordReminder зависит от абстракции, а не от конкретного движка базы данных.