Интерфейс — это контракт. Он не содержит реализации, но строго предписывает: любой класс, взявший на себя этот контракт, обязан реализовать каждый из объявленных методов. Именно интерфейсы позволяют писать код, который работает с множеством разных объектов одинаково — не зная и не заботясь об их внутреннем устройстве.
Что такое интерфейс и зачем он нужен
Представьте, что вы пишете систему отправки уведомлений. Уведомления могут уходить по email, через SMS, в Telegram. Способы доставки разные, но у каждого есть общее: метод send(). Интерфейс позволяет закрепить этот «общий знаменатель» как обязательное требование — и писать код, который работает с любым типом уведомлений без привязки к конкретному классу.
Объявление и реализация интерфейса
Интерфейс объявляется ключевым словом interface. Методы внутри него не имеют тела — только сигнатуру. Класс принимает контракт через ключевое слово implements и обязан реализовать каждый метод интерфейса, иначе PHP выбросит фатальную ошибку.
interface Notifiable
{
public function send(): void;
public function getRecipient(): string;
}
class EmailNotification implements Notifiable
{
public function __construct(
private readonly string $email,
private readonly string $message
) {}
public function send(): void
{
echo "Email → {$this->email}: {$this->message}\n";
}
public function getRecipient(): string
{
return $this->email;
}
}
class SmsNotification implements Notifiable
{
public function __construct(
private readonly string $phone,
private readonly string $message
) {}
public function send(): void
{
echo "SMS → {$this->phone}: {$this->message}\n";
}
public function getRecipient(): string
{
return $this->phone;
}
}
Теперь напишем функцию, которая принимает любой объект, реализующий Notifiable, — не важно, Email это или SMS:
// Функция работает с интерфейсом, а не с конкретным классом
function dispatch(Notifiable $notification): void
{
echo "Отправляем уведомление для: " . $notification->getRecipient() . "\n";
$notification->send();
}
dispatch(new EmailNotification('user@mail.ru', 'Заказ принят'));
dispatch(new SmsNotification('+7 900 000 00 00', 'Ваш код: 1234'));
// Вывод:
// Отправляем уведомление для: user@mail.ru
// Email → user@mail.ru: Заказ принят
// Отправляем уведомление для: +7 900 000 00 00
// SMS → +7 900 000 00 00: Ваш код: 1234
Когда функция принимает тип Notifiable, а не EmailNotification, она становится независимой от конкретной реализации. Можно добавить TelegramNotification или PushNotification — функцию менять не придётся.
Множественная реализация интерфейсов
В отличие от классов, которые могут наследовать только один родительский класс, один класс вправе реализовывать сколько угодно интерфейсов. Это мощный инструмент для построения гибкой архитектуры
interface Loggable
{
public function getLogMessage(): string;
}
interface Queueable
{
public function getQueue(): string;
}
// Один класс — три интерфейса
class EmailNotification implements Notifiable, Loggable, Queueable
{
public function __construct(
private readonly string $email,
private readonly string $message
) {}
public function send(): void
{
echo "Email → {$this->email}\n";
}
public function getRecipient(): string
{
return $this->email;
}
public function getLogMessage(): string
{
return "[EMAIL] Отправлено на {$this->email}";
}
public function getQueue(): string
{
return 'notifications';
}
}
$n = new EmailNotification('a@b.ru', 'Добро пожаловать!');
var_dump($n instanceof Notifiable); // true
var_dump($n instanceof Loggable); // true
var_dump($n instanceof Queueable); // true
Наследование интерфейсов
Интерфейс может расширять другой интерфейс через extends. Это позволяет строить иерархии контрактов: от общего к частному.
interface Sender
{
public function send(): void;
}
// Расширяем базовый интерфейс — добавляем метод повтора
interface RetrySender extends Sender
{
public function retry(int $attempts): void;
}
// Класс обязан реализовать оба метода: send() и retry()
class RobustEmailSender implements RetrySender
{
public function send(): void
{
echo "Отправка письма...\n";
}
public function retry(int $attempts): void
{
echo "Повторная отправка, попытка: {$attempts}\n";
}
}
$sender = new RobustEmailSender();
$sender->send(); // Отправка письма...
$sender->retry(2); // Повторная отправка, попытка: 2
var_dump($sender instanceof Sender); // true
var_dump($sender instanceof RetrySender); // true
Интерфейс не может содержать свойства — только методы и константы. Попытка объявить свойство внутри интерфейса вызовет синтаксическую ошибку. Для свойств используйте абстрактные классы.
Интерфейс — это соглашение между частями программы. Он говорит: «Не важно, как именно ты работаешь внутри. Важно, что у тебя есть метод send() с вот такой сигнатурой». Это позволяет писать код, который легко расширяется: новый тип уведомлений, новый способ оплаты, новый источник данных — достаточно реализовать нужный интерфейс.
В PHP 8 интерфейсы стали ещё удобнее: типизация возвращаемых значений, тип mixed, never и объединённые типы расширяют выразительность контрактов. Интерфейсы, основа таких архитектурных принципов, как Dependency Inversion и принцип разделения интерфейсов из набора SOLID.
0 Комментарий(я)
Зарегистрируйтесь чтобы оставить комментарий