Статья

📘 Глава 22. Интерфейсы, контракты и принципы SOLID

Переходим к архитектурному фундаменту ООП.


🔌 Что такое интерфейс?

Интерфейс — это контракт, который говорит: «если ты реализуешь меня, то ты обязан создать такие-то методы».

Интерфейс не содержит реализации, только сигнатуры методов.


🧪 Пример:

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.

PHP
Для ответа вы можете авторизоваться