Интерфейсы

Под «интерфейсом» в языках программирования обычно понимают обязательство класса по реализации функционала (набора методов), который описан в интерфейсе.

Интерфейсы похожи на классы, но у них есть отличительные особенности:

  • интерфейс определяется с помощью ключевого слова «interface»;
  • интерфейс не содержит реализации методов, а только их определения;
  • все методы интерфейса должны быть публичными.

Подробнее об особенностях реализации интерфейсов в PHP можно узнать из официальной документации.

<?php
 
interface FirstInterface
{
    const CONST_NAME = 'Константа из MyFirstInterface';
 
    public function firstMethod();
    public function secondMethodWithParams(int $paramFirst, string $paramSecond);
}
 
interface SecondInterface extends FirstInterface
{
    public function thirdMethod();
}
 
class MyClass implements SecondInterface
{
    public function firstMethod()
    {
        echo 'firstMethod()' . PHP_EOL;
    }
 
    public function secondMethodWithParams(int $paramFirst, string $paramSecond)
    {
        echo 'secondMethodWithParams(int $paramFirst, string $paramSecond)' . PHP_EOL;
    }
 
    public function thirdMethod()
    {
        echo 'thirdMethod()' . PHP_EOL;
    }
}
 
$object = new MyClass();
$object->firstMethod();
$object->secondMethodWithParams(1, 'two');
$object->thirdMethod();
 
echo $object::CONST_NAME;

В реализации механизма интерфейсов на PHP есть свои особенности, например: так как в PHP отсутствует перегрузка методов, то нельзя наследовать интерфейсы с одинаковыми наименованиями методов и разными параметрами. Приведу пример с ошибкой наследования интерфейсов из-за несовпадения сигнатур (определений) методов:

<?php
 
interface FirstInterface
{
    public function firstFunction();
    public function secondFunction();
}
 
interface SecondInterface extends FirstInterface
{
    public function firstFunction();
    public function secondFunction(int $age); // Возникнет ошибка, т.к. в новом варианте "secondFunction" есть параметр "$age".
}

С созданием интерфейсов определились, теперь осталось разобраться как их использовать в реальных проектах. Для примера, рассмотрим следующую ситуацию: у нас есть класс «UserManager», который занимается управлением пользователями и который использует класс «SimpleNotifier» для оповещения пользователей по E-mail. Вот так выглядит его работа:

<?php
 
class SimpleNotifier
{
    public function send()
    {
        echo 'Отправка сообщения на E-mail.';
    }
}
 
class UserManager
{
    private $notifier;
 
    public function __construct(SimpleNotifier $notifier)
    {
        $this->notifier= $notifier;
    }
 
    public function do()
    {
        // другие действия.
        $this->notifier->send();
        // другие действия.
    }
}
 
$manager = new UserManager(new SimpleNotifier());
$manager->do();

Чтобы «научить» класс «UserManager» отправлять сообщения не только на E-mail, но и на другие устройства, нам необходимо абстрагироваться от класса «SimpleNotifier» с помощью интерфейса. Через интерфейс можно будет передавать любой объект, который реализует функционал объявленный в этом интерфейсе. Вот как будет это выглядеть на нашем примере:

<?php
 
interface NotifierInterface
{
    public function send();
}
 
class SmsNotifier implements NotifierInterface
{
    public function send()
    {
        echo 'Отправка sms на телефон.';
    }
}
 
 
class EmailNotifier implements NotifierInterface
{
    public function send()
    {
        echo 'Отправка письма на E-mail.';
    }
}
 
class UserManager
{
    private $notifier;
 
    // Вместо класса (SimpleNotifier) передаем интерфейс (NotifierInterface). 
    public function __construct(NotifierInterface $notifier)
    {
        $this->notifier = $notifier;
    }
 
    public function do()
    {
        // другие действия.
        $this->notifier->send();
        // другие действия.
    }
}
 
$manager1 = new UserManager(new SmsNotifier());
$manager1->do();
 
$manager2 = new UserManager(new EmailNotifier());
$manager2->do();

Пример, конечно, крайне упрощен, но смысл должен быть понятен. Через использование интерфейсов вместо классов можно получить свободу от конкретных реализаций, что упростит наращивание и модификацию функционала.

При использовании интерфейсов, как и ООП в общем, нужно знать меру, не создавайте интерфейсы на пустом месте, их использование должно быть оправдано. Как понять, оправдано ли использование интерфейса в выбранном месте или нет? Вопрос сложный, и ответы на него приходят с опытом. Для начала, перед введением интерфейса, стоит задавать себе такой вопрос — «Планируется ли значительно наращивать функционал?».

03.01.2020

Категория(-и): PHP, ООП

# # #

Добавить комментарий