Интерфейсы позволяют создавать код, который определяет методы классов, которые его реализуют. Однако вы не можете добавить код к этим методам.
Абстрактные классы позволяют вам делать то же самое, наряду с добавлением кода в метод.
Теперь, если вы можете достичь той же цели с помощью абстрактных классов, зачем нам вообще нужна концепция интерфейсов?
Мне сказали, что это связано с теорией ОО от C ++ до Java, на которой основаны ООП в PHP. Является ли эта концепция полезной в Java, но не в PHP? Это просто способ избежать засорения заполнителей в абстрактном классе? Я что-то упускаю?
Ответы:
Весь смысл интерфейсов состоит в том, чтобы дать вам гибкость, чтобы ваш класс был вынужден реализовать несколько интерфейсов, но все же не разрешать множественное наследование. Проблемы с наследованием от нескольких классов многочисленны и разнообразны, и страница в Википедии на них довольно хорошо их суммирует.
Интерфейсы - это компромисс. Большинство проблем с множественным наследованием не относятся к абстрактным базовым классам, поэтому большинство современных языков в настоящее время отключают множественное наследование, но при этом вызывают интерфейсы абстрактных базовых классов и позволяют классу «реализовывать» столько, сколько они хотят.
источник
Эта концепция полезна в объектно-ориентированном программировании. Для меня я думаю об интерфейсе как о контракте. До тех пор, пока мой класс и ваш класс договариваются об этом контракте подписи метода, мы можем «взаимодействовать». Что касается абстрактных классов, то я считаю их более базовыми классами, которые ограничивают некоторые методы, и мне нужно заполнить детали.
источник
Зачем вам интерфейс, если уже есть абстрактные классы? Для предотвращения множественного наследования (может вызвать несколько известных проблем).
Одна из таких проблем:
Источник: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem
Почему / Когда использовать интерфейс? Пример ... Все машины в мире имеют одинаковый интерфейс (методы) ...
AccelerationPedalIsOnTheRight()
,BrakePedalISOnTheLeft()
. Представьте, что у каждой автомобильной марки эти "методы" будут отличаться от другой марки. У БМВ были бы тормоза на правой стороне, и у Хонды были бы тормоза на левой стороне колеса. Люди должны были бы узнать, как работают эти «методы» каждый раз, когда покупали автомобиль другой марки. Вот почему хорошая идея иметь один и тот же интерфейс в нескольких «местах».Что интерфейс делает для вас (почему кто-то даже использовал бы его)? Интерфейс не позволяет вам делать «ошибки» (он гарантирует, что все классы, которые реализуют определенный интерфейс, будут иметь методы, которые находятся в интерфейсе).
Таким образом,
Create()
метод всегда будет использоваться одинаково. Неважно, если мы используемMySqlPerson
класс илиMongoPerson
класс. То, как мы используем метод, остается прежним (интерфейс остается прежним).Например, он будет использоваться следующим образом (везде в нашем коде):
Таким образом, что-то подобное не может произойти:
Намного проще запомнить один интерфейс и использовать его везде, чем несколько разных.
Таким образом, внутренняя часть
Create()
метода может быть разной для разных классов, не влияя на «внешний» код, который вызывает этот метод. Внешний код должен знать только то, что методCreate()
имеет 1 параметр ($personObject
), потому что именно так внешний код будет использовать / вызывать метод. Внешний код не заботится о том, что происходит внутри метода; это только должно знать, как использовать / назвать это.Вы можете сделать это и без интерфейса, но если вы используете интерфейс, он «безопаснее» (потому что он предотвращает ошибки). Интерфейс гарантирует, что метод
Create()
будет иметь одинаковую сигнатуру (одинаковые типы и одинаковое количество параметров) во всех классах, которые реализуют интерфейс. Таким образом, вы можете быть уверены, что ЛЮБОЙ класс, который реализуетIPersonService
интерфейс, будет иметь методCreate()
(в этом примере) и будет нуждаться только в 1 параметре ($personObject
) для вызова / использования.Класс, который реализует интерфейс, должен реализовать все методы, которые интерфейс делает / имеет.
Я надеюсь, что я не повторял себя слишком много.
источник
Разница между использованием интерфейса и абстрактного класса больше для меня связана с организацией кода, чем с применением самого языка. Я часто использую их при подготовке кода для работы с другими разработчиками, чтобы они не выходили за рамки предполагаемых шаблонов проектирования. Интерфейсы - это своего рода «проектирование по контракту», при котором ваш код соглашается отвечать на предписанный набор вызовов API, которые могут исходить из кода, к которому у вас нет доступа.
Хотя наследование от абстрактного класса является отношением «является», это не всегда то, что вам нужно, а реализация интерфейса - это скорее отношение «действует как». Эта разница может быть довольно значительной в определенных контекстах.
Например, допустим, у вас есть абстрактный класс Account, из которого расширяются многие другие классы (типы учетных записей и т. Д.). У него есть определенный набор методов, которые применимы только к этой группе типов. Однако некоторые из этих подклассов учетных записей реализуют Versionable, Listable или Editable, чтобы их можно было бросать в контроллеры, которые ожидают использования этих API. Контроллеру все равно, какой это тип объекта
В отличие от этого, я также могу создать объект, который не выходит за пределы Account, скажем, абстрактный класс User, и при этом реализовать Listable и Editable, но не Versionable, что здесь не имеет смысла.
Таким образом, я говорю, что подкласс FooUser НЕ является учетной записью, но действует как редактируемый объект. Аналогично, BarAccount расширяется от Account, но не является подклассом User, но реализует Editable, Listable и также Versionable.
Добавление всех этих API-интерфейсов для Editable, Listable и Versionable в сами абстрактные классы не только будет беспорядочным и уродливым, но либо приведет к дублированию общих интерфейсов в Account и User, либо вынудит мой объект User реализовать Versionable, возможно, просто для исключение.
источник
Интерфейсы - это, по сути, план того, что вы можете создать. Они определяют, какие методы должен иметь класс , но вы можете создавать дополнительные методы вне этих ограничений.
Я не уверен, что вы имеете в виду, не имея возможности добавлять код в методы - потому что вы можете. Применяете ли вы интерфейс к абстрактному классу или к классу, который его расширяет?
Метод в интерфейсе, применяемый к абстрактному классу, должен быть реализован в этом абстрактном классе. Однако примените этот интерфейс к расширяющему классу, и метод требует реализации только в расширяющем классе. Я могу ошибаться - я не использую интерфейсы так часто, как мог бы / должен.
Я всегда думал об интерфейсах как о шаблоне для внешних разработчиков или о дополнительном наборе правил, чтобы убедиться, что все правильно.
источник
Вы будете использовать интерфейсы в PHP:
$object instanceof MyInterface
Car
объект может теперьstart()
,stop()
(EngineInterface) илиgoRight()
,goLeft()
(интерфейс управления)и другие вещи, о которых я не могу думать прямо сейчас
Номер 4, пожалуй, самый очевидный вариант использования, к которому нельзя обращаться с абстрактными классами.
Из мышления на Java:
источник
Интерфейсы существуют не как база для расширения классов, а как карта необходимых функций.
Ниже приведен пример использования интерфейса, в котором абстрактный класс не подходит:
допустим, у меня есть приложение календаря, которое позволяет пользователям импортировать данные календаря из внешних источников. Я написал бы классы для обработки импорта каждого типа источника данных (ical, rss, atom, json). Каждый из этих классов реализовывал бы общий интерфейс, который гарантировал бы, что все они имеют общие публичные методы, необходимые моему приложению для получения данных.
Затем, когда пользователь добавляет новый фид, я могу определить тип фида и использовать класс, разработанный для этого типа, для импорта данных. Каждый класс, написанный для импорта данных для определенного фида, будет иметь совершенно другой код, в противном случае между классами может быть очень мало общего, за исключением того факта, что они необходимы для реализации интерфейса, позволяющего моему приложению использовать их. Если бы я использовал абстрактный класс, я мог бы очень легко проигнорировать тот факт, что я не переопределил метод getEvents (), который затем нарушил бы мое приложение в этом случае, тогда как использование интерфейса не позволило бы моему приложению работать, если ЛЮБОЙ из методов определенные в интерфейсе не существуют в классе, который его реализовал. Моему приложению не нужно заботиться о том, какой класс он использует для получения данных из канала,
Чтобы продвинуться дальше, интерфейс оказывается чрезвычайно полезным, когда я возвращаюсь к своему календарному приложению с намерением добавить другой тип канала. Использование интерфейса ImportableFeed означает, что я могу продолжать добавлять больше классов, которые импортируют различные типы каналов, просто добавляя новые классы, которые реализуют этот интерфейс. Это позволяет мне добавлять тонны функциональности, не прибегая к ненужным объемам в моем базовом приложении, поскольку мое базовое приложение зависит только от наличия открытых методов, необходимых для интерфейса, так как мои новые классы импорта каналов реализуют интерфейс ImportableFeed, тогда я знаю, я могу просто бросить его на место и продолжать двигаться.
Это просто очень простое начало. Затем я могу создать другой интерфейс, который могут потребоваться для реализации всех моих классов календаря, который предлагает больше функций, специфичных для типа фида, который обрабатывает класс. Другим хорошим примером может быть метод проверки типа корма и т. Д.
Это выходит за рамки вопроса, но поскольку я использовал приведенный выше пример: интерфейсы поставляются со своим собственным набором проблем, если используются таким образом. Я считаю, что мне нужно обеспечить вывод, возвращаемый из методов, реализованных для соответствия интерфейсу, и для достижения этого я использую IDE, которая читает блоки PHPDoc и добавляю тип возврата в качестве подсказки типа в блоке PHPDoc интерфейса, который затем перевести на конкретный класс, который его реализует. Мои классы, которые используют выходные данные классов, которые реализуют этот интерфейс, будут, по крайней мере, знать, что ожидают массив, возвращенный в этом примере:
Не так много места для сравнения абстрактных классов и интерфейсов. Интерфейсы - это просто карты, которые при реализации требуют, чтобы класс имел набор открытых интерфейсов.
источник
Интерфейсы предназначены не только для того, чтобы разработчики реализовывали определенные методы. Идея состоит в том, что, поскольку эти классы гарантированно имеют определенные методы, вы можете использовать эти методы, даже если вы не знаете фактический тип класса. Пример:
Во многих случаях не имеет смысла предоставлять базовый класс, абстрактный или нет, потому что реализации сильно различаются и не имеют ничего общего, кроме нескольких методов.
Языки с динамической типизацией имеют понятие «типизированная утка», когда вам не нужны интерфейсы; Вы можете предположить, что у объекта есть метод, который вы вызываете. Это решает проблему в статически типизированных языках, где у вашего объекта есть какой-то метод (в моем примере read ()), но не реализован интерфейс.
источник
На мой взгляд, интерфейсы должны быть предпочтительнее, чем нефункциональные абстрактные классы. Я не был бы удивлен, если бы там был даже удар производительности, поскольку есть только один экземпляр объекта, вместо того, чтобы анализировать два, комбинируя их (хотя, я не могу быть уверен, я не знаком с внутренней работой) ООП PHP).
Это правда, что интерфейсы менее полезны / значимы, чем, скажем, Java. С другой стороны, PHP6 введет еще больше хинтинга типов, включая хинтинг типов для возвращаемых значений. Это должно добавить некоторую ценность интерфейсам PHP.
tl; dr: interfaces определяет список методов, которым необходимо следовать (например, API), в то время как абстрактный класс предоставляет некоторые основные / общие функциональные возможности, которые подклассы уточняют для конкретных потребностей.
источник
В PHP вы можете применять несколько интерфейсов, разделяя их запятой (я думаю, я не считаю это чистым решением).
Что касается нескольких абстрактных классов, у вас может быть несколько абстрактов, расширяющих друг друга (опять же, я не совсем уверен в этом, но я думаю, что видел это где-то раньше). Единственное, что вы не можете продлить, это последний класс.
источник
Интерфейсы не дадут вашему коду никакого повышения производительности или чего-то в этом роде, но они могут значительно улучшить его поддержку. Это правда, что абстрактный класс (или даже неабстрактный класс) можно использовать для установления интерфейса с вашим кодом, но правильные интерфейсы (те, которые вы определяете с помощью ключевого слова и которые содержат только сигнатуры методов) просто проще перебрать и прочитать.
При этом я склонен проявлять осмотрительность при принятии решения, использовать ли интерфейс над классом. Иногда мне нужны реализации методов по умолчанию или переменные, которые будут общими для всех подклассов.
Конечно, вопрос о реализации многоинтерфейса также является обоснованным. Если у вас есть класс, который реализует несколько интерфейсов, вы можете использовать объект этого класса в качестве разных типов в одном приложении.
Тот факт, что ваш вопрос касается PHP, делает вещи немного более интересными. Печатание на интерфейсах все еще не является чрезвычайно необходимым в PHP, где вы можете в значительной степени передать любой метод, независимо от его типа. Вы можете статически набирать параметры метода, но некоторые из них не работают (я думаю, что String вызывает некоторые сбои). Соедините это с тем фактом, что вы не можете печатать большинство других ссылок, и нет особой ценности в попытках принудительной статической типизации в PHP ( на данный момент ). И из-за этого, значение интерфейсов в PHP , на данный моментгораздо меньше, чем в более строго типизированных языках. Они имеют преимущество читабельности, но мало что еще. Многократная реализация даже не выгодна, потому что вы все равно должны объявлять методы и предоставлять им тела в реализаторе.
источник
Ниже приведены пункты для интерфейса PHP
Пример кода:
источник
Мы видели, что абстрактные классы и интерфейсы похожи в том, что они предоставляют абстрактные методы, которые должны быть реализованы в дочерних классах. Тем не менее, они по-прежнему имеют следующие различия:
Надеюсь, что это поможет любому понять!
источник
Интерфейсы как твои гены.
Абстрактные классы как твои настоящие родители.
Их цели наследственные, но в случае абстрактных классов и интерфейсов то, что наследуется, более конкретно.
источник
Я не знаю о других языках, какова концепция интерфейса там. Но для PHP, я постараюсь объяснить это. Просто наберитесь терпения и, пожалуйста, прокомментируйте, если это помогло.
Правило
Теперь давайте возьмем пример. Предположим, у нас есть две игрушки: одна - собака, а другая - кошка.
Как мы знаем, собака лает, а кошка мяукает. Эти два имеют одинаковый метод разговора, но с разной функциональностью или реализацией. Предположим, мы даем пользователю пульт дистанционного управления с кнопкой разговора.
Это хороший случай, чтобы использовать интерфейс, а не абстрактный класс, потому что реализации разные. Зачем? Помните
Если вам нужно поддержать дочерние классы, добавив неабстрактный метод, вы должны использовать абстрактные классы. В противном случае, интерфейс будет вашим выбором.
источник