📘 Глава 22. Интерфейсы, контракты и принципы SOLID
- 🔌 Что такое интерфейс
- 🧠 Интерфейсы ≠ абстрактные классы
- 🧰 Реальный пример: интерфейс репозитория
- 📌 Внедрение зависимостей (Dependency Injection)
- 🧱 Принципы SOLID
- 🟠 S — Single Responsibility Principle
- 🔵 O — Open/Closed Principle
- 🟢 L — Liskov Substitution Principle
- 🟣 I — Interface Segregation Principle
- 🔴 D — Dependency Inversion Principle
- ✅ Что ты усвоил
- ⏭ В следующей главе
Переходим к архитектурному фундаменту ООП.
🔌 Что такое интерфейс?
Интерфейс — это контракт, который говорит: «если ты реализуешь меня, то ты обязан создать такие-то методы».
Интерфейс не содержит реализации, только сигнатуры методов.
🧪 Пример:
interface Loggable {
public function log(string $message): void;
}
Класс, реализующий интерфейс:
class FileLogger implements Loggable {
public function log(string $message): void {
file_put_contents('log.txt', $message . PHP_EOL, FILE_APPEND);
}
}
Теперь мы уверены, что любой Loggable
имеет метод log
.
📦 Преимущество интерфейсов:
-
Обеспечивают гибкость
-
Позволяют писать код, не зависящий от реализации
-
Помогают при замене, тестировании, внедрении зависимостей
🧠 Интерфейсы ≠ абстрактные классы
Интерфейс | Абстрактныйкласс |
---|---|
Реализация методов | ❌ Нет |
Свойства | ❌ Нет |
Множественная реализация | ✅ Да |
Назначение | Контракт |
🧰 Реальный пример: интерфейс репозитория
interface UserRepositoryInterface {
public function findById(int $id): array;
}
Реализация:
class MySQLUserRepository implements UserRepositoryInterface {
public function findById(int $id): array {
// Достаём из базы данных
}
}
📌 Внедрение зависимостей (Dependency Injection)
Вместо:
$userRepo = new MySQLUserRepository();
Лучше передать зависимость:
class UserController {
public function __construct(
private UserRepositoryInterface $repo
) {}
public function show(int $id) {
$user = $this->repo->findById($id);
}
}
Теперь можно подменить repo
на фейковый класс для тестов или использовать другую БД.
🧱 Принципы SOLID
🟠 S — Single Responsibility Principle
Класс должен иметь одну причину для изменения.
❌ Плохо:
class User {
public function saveToDatabase() {}
public function validateEmail() {}
public function sendWelcomeEmail() {}
}
✅ Хорошо:
-
UserValidator
-
UserRepository
-
EmailSender
🔵 O — Open/Closed Principle
Классы должны быть открыты для расширения, но закрыты для изменения.
❌ Неудобно:
if ($type === 'email') sendEmail();
else if ($type === 'sms') sendSms();
✅ Лучше использовать стратегии:
interface Notifier {
public function notify(string $message): void;
}
🟢 L — Liskov Substitution Principle
Объекты подклассов должны заменять объекты родителя без ошибок.
Если Duck
наследуется от Bird
, она должна вести себя как Bird
, не ломая поведение.
🟣 I — Interface Segregation Principle
Лучше иметь много мелких интерфейсов, чем один большой.
❌ Плохо:
interface Animal {
public function fly();
public function swim();
}
✅ Лучше:
interface CanFly {}
interface CanSwim {}
🔴 D — Dependency Inversion Principle
Зависимости должны быть от абстракций, а не от конкретик.
✅ Зависим от UserRepositoryInterface
, а не от MySQLUserRepository
.
✅ Что ты усвоил:
-
Как создавать и применять интерфейсы
-
Отличие интерфейсов от абстрактных классов
-
Основы внедрения зависимостей
-
Принципы SOLID и зачем они нужны
⏭ В следующей главе:
Поговорим о трейтах (traits) — способе повторного использования кода между классами без наследования, и о магических методах вроде __get
, __call
, __toString
.