Вопрос

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

Абстрактные классы позволяет вам делать то же самое, наряду с добавлением кода в метод.

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

Мне сказали, что это связано с теорией OO от C ++ до Java, на которой основан OO материал PHP.Полезна ли эта концепция в Java, но не в PHP?Это просто способ избежать разбросанности заполнителей в абстрактном классе?Я что-то упускаю?

Это было полезно?

Решение

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

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

Другие советы

Эта концепция повсеместно полезна в объектно-ориентированном программировании.Для меня я думаю об интерфейсе как о контракте.До тех пор, пока мой класс и ваш класс согласуют этот контракт подписи метода, мы можем "взаимодействовать".Что касается абстрактных классов, то я рассматриваю их как нечто большее, чем базовые классы, которые заглушают некоторые методы, и мне нужно заполнить детали.

Зачем вам нужен интерфейс, если уже есть абстрактные классы? Чтобы предотвратить множественное наследование (может вызвать множество известных проблем).

Одна из таких проблем:

"Алмазная проблема" (иногда называемая "смертоносным алмазом смерти") - это двусмысленность, возникающая, когда два класса B и C наследуют от A, а класс D наследует как от B, так и от C.Если в A есть метод , который B и C переопределили, а D его не переопределяет, то какую версию метода D наследует:тот, что у В, или тот, что у С?

Источник: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

Зачем и когда использовать интерфейс? Вот пример...Все автомобили в мире имеют одинаковый интерфейс (методы)... AccelerationPedalIsOnTheRight(), BrakePedalISOnTheLeft().Представьте, что у каждой марки автомобиля эти "методы" отличались бы от других марок.У BMW были бы тормоза с правой стороны, а у Honda - с левой.Людям пришлось бы узнавать, как работают эти "методы", каждый раз, когда они покупали бы машину другой марки.Вот почему это хорошая идея - иметь один и тот же интерфейс в нескольких "местах".

Что делает интерфейс для вас (зачем кому-то вообще им пользоваться)?Интерфейс предотвращает совершение вами "ошибок" (он гарантирует вам, что все классы, реализующие определенный интерфейс, будут иметь методы, которые есть в интерфейсе).

// Methods inside this interface must be implemented in all classes which implement this interface.
interface IPersonService
{   
    public function Create($personObject);
}

class MySqlPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Create a new person in MySql database.
    }
}

class MongoPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Mongo database creates a new person differently then MySQL does. But the code outside of this method doesn't care how a person will be added to the database, all it has to know is that the method Create() has 1 parameter (the person object).
    }
}

Таким образом, Create() метод всегда будет использоваться одинаково.Не имеет значения, используем ли мы MySqlPerson класс или MongoPerson класс.Способ, которым мы используем метод, остается прежним (интерфейс остается прежним).

Например, он будет использоваться следующим образом (везде в нашем коде).:

new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);

Таким образом, что-то подобное не может произойти:

new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);

Гораздо проще запомнить один интерфейс и везде использовать один и тот же, чем несколько разных.

Таким образом, внутренняя часть Create() метод может быть разным для разных классов, не влияя на "внешний" код, который вызывает этот метод.Все, что должен знать внешний код, это то, что метод Create() имеет 1 параметр ($personObject), потому что именно так внешний код будет использовать / вызывать метод.Внешнему коду все равно, что происходит внутри метода;ему нужно только знать, как его использовать / вызывать.

Вы можете сделать это и без интерфейса, но если вы используете интерфейс, это "безопаснее" (потому что это предотвращает ваши ошибки).Интерфейс гарантирует вам, что метод Create() будет иметь одинаковую сигнатуру (одинаковые типы и одинаковое количество параметров) во всех классах, реализующих интерфейс.Таким образом, вы можете быть уверены, что ЛЮБОЙ класс, реализующий IPersonService интерфейс, будет иметь метод Create() (в этом примере) и потребуется только 1 параметр ($personObject), чтобы его вызывали / использовали.

Класс, реализующий интерфейс, должен реализовывать все методы, которые выполняет / имеет интерфейс.

Надеюсь, я не слишком часто повторялся.

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

Хотя наследование от абстрактного класса - это отношение "есть a", это не всегда то, что вы хотите, и реализация интерфейса - это скорее отношение "действует как a".Это различие может быть весьма значительным в определенных контекстах.

Например, предположим, у вас есть абстрактный класс Account, из которого расширяются многие другие классы (типы учетных записей и так далее).У него есть определенный набор методов, которые применимы только к этой группе типов.Однако некоторые из этих подклассов учетных записей реализуют возможность проверки версий, или перечисления, или редактирования, чтобы их можно было передавать в контроллеры, которые ожидают использования этих API.Контроллеру все равно, к какому типу относится данный объект

В отличие от этого, я также могу создать объект, который не распространяется на Account, скажем, абстрактный класс User, и по-прежнему реализовывать Listable и Editable, но не Versionable, что здесь не имеет смысла.

Таким образом, я говорю, что подкласс fooUser НЕ является учетной записью, но действует как редактируемый объект.Аналогично, BarAccount расширяется от Account, но не является подклассом User, но реализует Editable, Listable, а также Versionable.

Добавление всех этих API для Editable, Listable и Versionable в сами абстрактные классы было бы не только загроможденным и уродливым, но и либо дублировало бы общие интерфейсы в Account и User, либо заставило бы мой пользовательский объект реализовать Versionable, вероятно, просто для создания исключения.

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

Я не уверен, что вы имеете в виду, говоря о невозможности добавлять код к методам - потому что вы можете.Применяете ли вы интерфейс к абстрактному классу или к классу, который его расширяет?

Метод в интерфейсе, применяемый к абстрактному классу, должен быть реализован в этом абстрактном классе.Однако примените этот интерфейс к расширяющемуся классу, и метод нужно реализовать только в расширяющемся классе.Здесь я могу ошибаться - я использую интерфейсы не так часто, как мог бы / должен.

Я всегда думал об интерфейсах как о шаблоне для внешних разработчиков или дополнительном наборе правил для обеспечения правильности действий.

Вы будете использовать интерфейсы на PHP:

  1. Чтобы скрыть реализацию - установите протокол доступа к классу объектов и измените базовую реализацию без рефакторинга во всех местах, где вы использовали эти объекты.
  2. Для проверки типа - например, для того, чтобы убедиться, что параметр имеет определенный тип $object instanceof MyInterface
  3. Чтобы принудительно выполнить проверку параметров во время выполнения
  4. Реализовать несколько вариантов поведения в одном классе (создавать сложные типы)

    класс Car реализует интерфейс двигателя, интерфейс кузова, интерфейс рулевого управления {

так что a Car объект ca сейчас start(), stop() (Интерфейс двигателя) или goRight(),goLeft() (Интерфейс рулевого управления)

и другие вещи, о которых я не могу думать прямо сейчас

Номер 4 это, вероятно, самый очевидный вариант использования, который вы не можете решить с помощью абстрактных классов.

От мышления на Java:

Интерфейс говорит: “Вот как будут выглядеть все классы, реализующие этот конкретный интерфейс”. Таким образом, любой код, использующий определенный интерфейс, знает, какие методы могут быть вызваны для этого интерфейса, и это все.Таким образом, интерфейс используется для установления "протокола” между классами.

Интерфейсы существуют не как основа, на которую могут расширяться классы, а как карта требуемых функций.

Ниже приведен пример использования интерфейса, для которого абстрактный класс не подходит:
Допустим, у меня есть приложение-календарь, которое позволяет пользователям импортировать данные календаря из внешних источников.Я бы написал классы для обработки импорта каждого типа источника данных (ical, rss, atom, json), Каждый из этих классов реализовал бы общий интерфейс, который гарантировал бы, что все они имеют общие общедоступные методы, необходимые моему приложению для получения данных.

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Затем, когда пользователь добавляет новый канал, я могу определить тип этого канала и использовать класс, разработанный для этого типа, для импорта данных.Каждый класс, написанный для импорта данных для определенного канала, будет иметь совершенно другой код, в противном случае между классами может быть очень мало общего, за исключением того факта, что они необходимы для реализации интерфейса, который позволяет моему приложению использовать их.Если бы я использовал абстрактный класс, я мог бы очень легко игнорировать тот факт, что я не переопределил метод getEvents(), который затем прервал бы работу моего приложения в этом экземпляре, тогда как использование интерфейса не позволило бы моему приложению запускаться, если КАКОЙ-либо из методов, определенных в интерфейсе, не существует в классе, который его реализовал.Моему приложению не нужно заботиться о том, какой класс оно использует для получения данных из канала, важно только, чтобы присутствовали методы, необходимые ему для получения этих данных.

Чтобы сделать еще один шаг вперед, интерфейс оказывается чрезвычайно полезным, когда я возвращаюсь к своему приложению "Календарь" с намерением добавить другой тип ленты.Использование интерфейса ImportableFeed означает, что я могу продолжать добавлять новые классы, которые импортируют различные типы каналов, просто добавляя новые классы, реализующие этот интерфейс.Это позволяет мне добавлять тонны функциональности без необходимости добавлять ненужный объем в мое основное приложение, поскольку мое основное приложение зависит только от наличия общедоступных методов, которые требуются интерфейсу, поэтому, пока мои новые классы импорта каналов реализуют интерфейс ImportableFeed, я знаю, что могу просто оставить его на месте и продолжать двигаться.

Это всего лишь очень простое начало.Затем я могу создать другой интерфейс, для реализации которого могут потребоваться все мои классы календаря, который предлагает больше функциональности, специфичной для типа ленты, обрабатываемого классом.Другим хорошим примером может быть метод проверки типа подачи и т.д.

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

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

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

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

interface Readable {
  String read();
}

List<Readable> readables; // dunno what these actually are, but we know they have read();
for(Readable reader : readables)
  System.out.println(reader.read());

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

В динамически типизированных языках есть понятие "утиного ввода", когда вам не нужны интерфейсы;вы можете свободно предположить, что у объекта есть метод, который вы вызываете для него.Это позволяет обойти проблему в статически типизированных языках, где ваш объект имеет некоторый метод (в моем примере read()), но не реализует интерфейс.

На мой взгляд, интерфейсам следует отдавать предпочтение перед нефункциональными абстрактными классами.Я бы не удивился, если бы там было даже снижение производительности, поскольку создается только один экземпляр объекта, вместо того, чтобы разбирать два, комбинируя их (хотя, я не могу быть уверен, я не знаком с внутренней работой ООП PHP).

Это правда, что интерфейсы менее полезны / значимы, чем, скажем, Java.С другой стороны, PHP6 введет еще больше подсказок по типу, включая подсказку по типу для возвращаемых значений.Это должно добавить некоторую ценность интерфейсам PHP.

tl;dr:интерфейсы определяют список методов, которым необходимо следовать (например, API), в то время как абстрактный класс предоставляет некоторую базовую / общую функциональность, которую подклассы уточняют для конкретных нужд.

Я не могу вспомнить, отличается ли PHP в этом отношении, но в Java вы можете реализовать несколько интерфейсов, но вы не можете наследовать несколько абстрактных классов.Я бы предположил, что PHP работает точно так же.

В PHP вы можете применить несколько интерфейсов, разделив их запятой (я думаю, я не нахожу это чистым решением).

Что касается нескольких абстрактных классов, у вас может быть несколько абстракций, расширяющих друг друга (опять же, я не совсем уверен в этом, но мне кажется, я где-то это видел раньше).Единственное, что вы не можете расширить, - это конечный класс.

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

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

Конечно, замечание о реализации с несколькими интерфейсами тоже здравое.Если у вас есть класс, реализующий несколько интерфейсов, вы можете использовать объекты этого класса как разные типы в одном приложении.

Однако тот факт, что ваш вопрос касается PHP, делает ситуацию немного интереснее.Ввод текста в интерфейсы по-прежнему не является чем-то невероятно необходимым в PHP, где вы можете практически что угодно передать любому методу, независимо от его типа.Вы можете статически вводить параметры метода, но некоторые из них нарушены (String, я полагаю, вызывает некоторые сбои).Соедините это с тем фактом, что вы не можете вводить большинство других ссылок, и нет особого смысла пытаться принудительно вводить статическую типизацию в PHP (на данный момент).И из-за этого ценность интерфейсов возрастает в PHP, на данный момент это намного меньше, чем в более строго типизированных языках.У них есть преимущество в удобочитаемости, но мало что еще.Множественная реализация даже не приносит пользы, потому что вам все равно придется объявлять методы и предоставлять им тела внутри разработчика.

Интерфейсы подобны вашим генам.

Абстрактные классы похожи на ваших настоящих родителей.

Их цели являются наследственными, но в случае абстрактных классов и интерфейсов то, что наследуется, более конкретно.

Ниже приведены пункты для интерфейса PHP

  1. Он используется для определения требуемого отсутствия методов в классе [если вы хотите загрузить html, то требуются id и name, поэтому в этом случае интерфейс включает setID и setName].
  2. Интерфейс строго обязывает класс включать все методы, определенные в нем.
  3. Вы можете определить метод только в интерфейсе с общедоступным доступом.
  4. Вы также можете расширить интерфейс, например class.Вы можете расширить интерфейс в php, используя ключевое слово extends.
  5. Расширьте множественный интерфейс.
  6. Вы не можете реализовать 2 интерфейса, если оба совместно используют функцию с одинаковым именем.Это выдаст ошибку.

Пример кода :

interface test{
    public function A($i);
    public function B($j = 20);
}

class xyz implements test{
    public function A($a){
        echo "CLASS A Value is ".$a;
    }
    public function B($b){
        echo "CLASS B Value is ".$b;
    }
}
$x = new xyz();
echo $x->A(11);
echo "<br/>";
echo $x->B(10);

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

1. Интерфейсы могут включать абстрактные методы и константы, но не могут содержать конкретные методы и переменные.

2. Все методы в интерфейсе должны быть в публичный наглядность сфера применения.

3. Класс может реализовывать более одного интерфейса, в то время как он может наследовать только от одного абстрактного класса.

                                  interface                      abstract class
the code                     - abstract methods               - abstract methods
                             - constants                      - constants                  
                                                              - concrete methods
                                                              - concrete variables

access modifiers             
                             - public                         - public
                                                              - protected
                                                              - private
                                                                etc.
number of parents          The same class can implement
                           more than 1 interface              The child class can 
                                                              inherit only from 1 abstract class

Надеюсь, это поможет кому-нибудь понять!

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

Интерфейс работает как "контракты", указывающие, что делает набор подклассов, но не как они это делают.

Правило

  1. Интерфейс не может быть создан в виде экземпляра.

  2. Вы не можете реализовать какой-либо метод в интерфейсе, т.е.он содержит только сигнатуру метода, но не детали (тело).

  3. Интерфейсы могут содержать методы и / или константы, но не атрибуты.Константы интерфейса имеют те же ограничения, что и константы класса.Методы интерфейса неявно абстрактны.

  4. Интерфейсы не должны объявлять конструкторы или деструкторы, поскольку это детали реализации на уровне класса .
  5. Все методы в интерфейсе должны быть общедоступны.

Теперь давайте возьмем пример.Предположим, у нас есть две игрушки:один - Собака, а другой - Кошка.

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

Когда пользователь нажимает кнопку "Говорить", игрушка должна заговорить, не имеет значения, Собака это или Кошка.

Это хороший случай использовать интерфейс, а не абстрактный класс, потому что реализации разные.Почему?Вспомни

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top