Skip to content

Latest commit

 

History

History
executable file
·
83 lines (57 loc) · 7.69 KB

interfaces.md

File metadata and controls

executable file
·
83 lines (57 loc) · 7.69 KB

Интерфейсы в ООП

Интерфейс - это набор требований к классу. «Требование» здесь значит требование, чтобы в классе был определенный метод.

Если класс реализует интерфейс, в нем обязаны быть эти методы (иначе PHP укажет на ошибку). Обычно интерфейс представляет собой какое-то умение: классы, реализующие этот интерфейс, умеют что-то делать. Класс может реализовывать несколько интерфейсов.

Допустим, мы делаем сайт, где можно ставить лайки постам и комментам. Допустим, у нас есть классы User, Post и Comment. И, может быть, в будущем появятся новые сущности, которые тоже можно будет лайкать.

Вот у нас есть пост и коммент, мы можем увеличить число лайков, допустим методом addLike() и узнавать сколько у них лайков методом getLikeCount(), то есть у них есть что-то общее, но как описать это в коде? Как сказать, что эти 2 класса, в отличие от других, умеют работать с лайками?

Второй пример, мы хотим сделать функцию, которая определяет, есть ли у объекта хотя бы один лайк:

function hasLikes($object) { ... }

Как указать, что в качестве $object можно передать любой класс, который может принимать лайки?

И, наконец, как описать в коде требования, которым должен соответствовать объект, чтобы его можно было лайкать?

Эти проблемы решают интерфейсы. Объявим интерфейс Likeable ("лайкабельный"), который представляет собой умение получать лайки от зарегистрированных пользователей. Опишем, какие методы обязаны реализовать такие классы. Напишем комментарий к нему (советую всегда начинать написание классов и интерфейсов с комментария). Для комментариев я использовал синтаксис PHPDoc:

/**
 * Интерфейс представляет способность получать лайки.
 */
interface Likeable
{
    /**
     * Добавить объекту лайк от указанного пользователя.
     */   
    public function addLike(User $user): void;
    
    /**
     * Убрать лайк от указанного пользователя.
     */
    public function removeLike(User $user): void;
    
    /** 
     * Узнать, лайкал ли пользователь данный объект.
     */
    public function hasLiked(User $user): bool;
    
    /**
     * Получить число поставленных объекту лайков.
     */
    public function getLikeCount(): int;
}

Этот код написан для PHP7 и выше. Если используется PHP5, то нужно убрать тайп-хинты для возвращаемых значений вроде : void или : int, которые он не поддерживает.

Теперь укажем в коде, что посты и комменты можно лайкать:

class Post implements Likeable { ... }
class Comment implements Likeable { ... }

На этом этапе PHP проверит, не забыли ли мы реализовать в классах все упомянутые методы. Если забыли — выдаст ошибку. Как удобно!

Ну и теперь мы можем использовать интерфейс в качестве тайп-хинта, чтобы указать, какие аргументы принимает функция hasLikes:

function hasLikes(Likeable $object): bool 
{
    // нехитрая логика
    return $object->getLikeCount() > 0;
}

Так как мы указали в качестве тайп-хинта значение Likeable, то мы в функции можем вызывать у объекта только те методы, которые описаны в этом интерфейсы. Мы не должны как-то пытаться определить, какого конкретно класса нам передан объект. Мы не можем обращаться к его полям (они не описаны в интерфейсе). Это позволяет сохранить совместимость функции с новыми классами, которые будут написаны в будущем.

Благодаря интерфейсам наш код стал расширяем. В будущем мы сможем добавить, например, класс Photo, который тоже можно лайкать. Если он будет реализовывать интерфейс Likeable то функция вроде hasLikes() сможет работать и с ним без доработки кода.

В общем, интерфейс представляет какую-то способность класса и требует от него реализовать определенные методы. Интерфейсы часто используются в библиотеках, где автор хочет дать возможность пользователю создавать свои классы и использовать их вместе с библиотекой. Интерфейс позволяет описать требования, которым должны соответствовать эти классы.

При проектировании интерфейсов желательно делать на каждую "способность" свой интерфейс. Ну например, если мы захотим добавить к постам и комментариям кроме лайков возможность делать репост, то нужно сделать для этого отдельный интерфейс.

Имена интерфейсов формируются по тем же правилам, что и имена классов, но обычно они заканчиваются на -able (Likeable) или на Interface (LikeableInterface). В некоторых языках принято начинать имена с буквы I, например ILikeable. Интерфейсы можно размещать в неймспейсах. Интерфейсы можно наследовать от других интерфейсов (расширяя их), PHP поддерживает автозагрузку интерфейсов через загрузчик классов. В интерфейсах можно объявлять константы.

Подробно интерфейсы описаны в мануале PHP: http://php.net/manual/ru/language.oop5.interfaces.php