Интерфейсы существуют не как база, на которую могут расширяться классы, а как карта требуемых функций.
Ниже приведен пример использования интерфейса, в котором не подходит абстрактный класс:
Допустим, у меня есть приложение календаря, которое позволяет пользователям импортировать данные календаря из внешних источников. Я написал бы классы для обработки импорта каждого типа источника данных (ical, rss, atom, json). Каждый из этих классов реализовывал бы общий интерфейс, который гарантировал бы, что все они имеют общие публичные методы, необходимые моему приложению для получения данных.
<?php
interface ImportableFeed
{
public function getEvents();
}
Затем, когда пользователь добавляет новый фид, я могу определить тип фида и использовать класс, разработанный для этого типа, для импорта данных. Каждый класс, написанный для импорта данных для определенного фида, будет иметь совершенно другой код, в противном случае между классами может быть очень мало общего, за исключением того факта, что они необходимы для реализации интерфейса, который позволяет моему приложению использовать их. Если бы я использовал абстрактный класс, я мог бы очень легко проигнорировать тот факт, что я не переопределил метод getEvents (), который затем нарушил бы мое приложение в этом случае, тогда как использование интерфейса не позволило бы моему приложению работать, если ЛЮБОЙ из методов определенные в интерфейсе не существуют в классе, который его реализовал. Моему приложению не нужно заботиться о том, какой класс оно использует для получения данных из фида, только о наличии методов, необходимых для получения этих данных.
Чтобы продвинуться дальше, интерфейс оказывается чрезвычайно полезным, когда я возвращаюсь к своему календарному приложению с намерением добавить другой тип канала. Использование интерфейса ImportableFeed означает, что я могу продолжать добавлять больше классов, которые импортируют различные типы каналов, просто добавляя новые классы, которые реализуют этот интерфейс. Это позволяет мне добавлять тонны функциональности, не прибегая к ненужной массовой загрузке моего основного приложения, поскольку мое основное приложение полагается только на то, что доступны общедоступные методы, необходимые для интерфейса, если в моих новых классах импорта каналов реализован интерфейс ImportableFeed, тогда знаю, что я могу просто бросить его и продолжать двигаться.
Это просто очень простое начало. Затем я могу создать другой интерфейс, который может потребоваться для реализации всех моих классов календаря, который предлагает больше функций, специфичных для типа фида, который обрабатывает класс. Другим хорошим примером может служить метод проверки типа канала и т. Д.
Это выходит за рамки вопроса, но поскольку я использовал приведенный выше пример:
Интерфейсы поставляются с собственным набором проблем, если используются таким образом. Мне нужно убедиться, что выходные данные возвращаются из реализованных методов, чтобы соответствовать интерфейсу, и для этого я использую IDE, которая читает блоки PHPDoc и добавляю возвращаемый тип в качестве подсказки типа в блоке PHPDoc интерфейса, который затем перевести на конкретный класс, который его реализует. Мои классы, которые используют выходные данные классов, которые реализуют этот интерфейс, будут, по крайней мере, знать, что ожидают массив, возвращенный в этом примере:
<?php
interface ImportableFeed
{
/**
* @return array
*/
public function getEvents();
}
Не так много места для сравнения абстрактных классов и интерфейсов. Интерфейсы - это просто карты, которые при реализации требуют, чтобы класс имел набор открытых интерфейсов.