В последнее время я пытался изучать PHP и обнаружил, что зацикливаюсь на особенностях. Я понимаю концепцию горизонтального повторного использования кода и не хочу обязательно наследовать от абстрактного класса. Что я не понимаю, так это то, в чем заключается принципиальное отличие использования черт от интерфейсов?
Я пытался найти приличный пост в блоге или статью, объясняющую, когда использовать один или другой, но примеры, которые я нашел до сих пор, кажутся настолько похожими, что они идентичны.
Imagick
объекты, за исключением всего раздувания, необходимого в прежние времена до появления черт характера.Ответы:
Интерфейс определяет набор методов, которые должен реализовать реализующий класс .
Когда признак
use
d, реализации методов также встречаются - что не происходит вInterface
.Это самая большая разница.
Из горизонтального повторного использования для PHP RFC :
источник
Объявление о государственной службе:
Я хочу заявить, что черты почти всегда являются запахом кода и их следует избегать в пользу композиции. По моему мнению, одиночное наследование часто злоупотребляет до такой степени, что оно является анти-паттерном, а множественное наследование только усугубляет эту проблему. В большинстве случаев вам будет гораздо лучше обслуживаться, если вы предпочитаете композицию наследованию (будь то одно или несколько). Если вы все еще заинтересованы в особенностях и их связи с интерфейсами, читайте дальше ...
Давайте начнем с того, что скажем так:
Чтобы написать ОО-код, вы должны понимать, что ООП действительно зависит от возможностей ваших объектов. Вы должны думать о классах с точки зрения того, что они могут сделать вместо того, что они фактически делают . Это резко контрастирует с традиционным процедурным программированием, где основное внимание уделяется тому, чтобы немного кода «что-то делал».
Если ООП-код предназначен для планирования и проектирования, интерфейс - это проект, а объект - полностью построенный дом. Между тем, черты характера - это просто способ помочь построить дом, спроектированный планом (интерфейс).
Интерфейсы
Итак, почему мы должны использовать интерфейсы? Проще говоря, интерфейсы делают наш код менее хрупким. Если вы сомневаетесь в этом утверждении, спросите любого, кто был вынужден поддерживать устаревший код, который не был написан для интерфейсов.
Интерфейс - это контракт между программистом и его / ее кодом. Интерфейс говорит: «Пока вы играете по моим правилам, вы можете реализовывать меня так, как вам нравится, и я обещаю, что не нарушу ваш другой код».
В качестве примера рассмотрим реальный сценарий (без машин и виджетов):
Вы начинаете с написания класса для кэширования ответов на запросы с использованием APC:
Затем в своем объекте ответа HTTP вы проверяете наличие попадания в кэш, прежде чем выполнять всю работу по генерации фактического ответа:
Этот подход прекрасно работает. Но, может быть, через несколько недель вы решите использовать файловую кеш-систему вместо APC. Теперь вам нужно изменить код контроллера, потому что вы запрограммировали свой контроллер для работы с функциональностью
ApcCacher
класса, а не с интерфейсом, который выражает возможностиApcCacher
класса. Допустим, вместо вышесказанного вы сделалиController
класс зависимым, аCacherInterface
не конкретным,ApcCacher
вот так:Для этого вы определяете свой интерфейс следующим образом:
В свою очередь у вас есть и ваш,
ApcCacher
и ваши новыеFileCacher
классы, реализующие,CacherInterface
и вы программируете свойController
класс, чтобы использовать возможности, требуемые интерфейсом.Этот пример (надеюсь) демонстрирует, как программирование интерфейса позволяет вам изменять внутреннюю реализацию ваших классов, не беспокоясь о том, не повредят ли эти изменения ваш другой код.
Черты
Черты, с другой стороны, являются просто методом повторного использования кода. Интерфейсы не должны рассматриваться как взаимоисключающая альтернатива чертам. На самом деле, создание черт, которые соответствуют возможностям интерфейса, является идеальным вариантом использования .
Вы должны использовать черты, только когда несколько классов имеют одинаковую функциональность (вероятно, продиктовано одним и тем же интерфейсом). Нет смысла использовать черту для обеспечения функциональности для одного класса: это только запутывает то, что делает класс, и лучший дизайн переместит функциональность черты в соответствующий класс.
Рассмотрим следующую черту реализации:
Более конкретный пример: представьте, что и вы,
FileCacher
и вашApcCacher
из обсуждения интерфейса используют один и тот же метод, чтобы определить, является ли запись в кэше устаревшей и ее следует удалить (очевидно, это не так в реальной жизни, но используйте ее). Вы можете написать характеристику и позволить обоим классам использовать ее для общего требования интерфейса.Последнее слово предостережения: будьте осторожны, чтобы не переборщить с чертами. Часто черты используются в качестве опоры для плохого дизайна, когда реализации уникального класса будет достаточно. Вы должны ограничить черты в соответствии с требованиями интерфейса для лучшего дизайна кода.
источник
A
trait
- это, по сути, реализация PHP в amixin
, и, по сути, это набор методов расширения, которые могут быть добавлены в любой класс путем добавленияtrait
. Методы затем становятся частью реализации этого класса, но без использования наследования .Из руководства по PHP (выделено мое):
Пример:
Определив вышеуказанную черту, я могу сделать следующее:
На этом этапе, когда я создаю экземпляр класса
MyClass
, у него есть два метода, называемыеfoo()
иbar()
- которые берутсяmyTrait
. И - обратите внимание, чтоtrait
-определенные методы уже имеют тело метода, чего неInterface
может определить -определенный метод.Кроме того, PHP, как и многие другие языки, использует модель единого наследования, то есть класс может быть производным от нескольких интерфейсов, но не от нескольких классов. Тем не менее, класс PHP может иметь несколько
trait
включений, что позволяет программисту включать многократно используемые фрагменты, как они могли бы, если бы включали несколько базовых классов.Несколько вещей, на которые стоит обратить внимание:
Полиморфизм:
В предыдущем примере, в котором
MyClass
проходитSomeBaseClass
,MyClass
является экземпляромSomeBaseClass
. Другими словами, такой массивSomeBaseClass[] bases
может содержать экземплярыMyClass
. Точно так же, еслиMyClass
расширенIBaseInterface
, массивIBaseInterface[] bases
может содержать экземплярыMyClass
. Нет такой полиморфной конструкции, доступной сtrait
- потому что поtrait
сути это просто код, который для удобства программиста копируется в каждый класс, который его использует.Внеочередные:
Как описано в руководстве:
Итак - рассмотрим следующий сценарий:
При создании экземпляра MyClass, выше, происходит следующее:
Interface
IBase
Требует функцию без параметров под названиемSomeMethod()
должны быть предоставлены.BaseClass
обеспечивает реализацию этого метода - удовлетворение потребности.trait
myTrait
Обеспечивает функцию без параметров , называемойSomeMethod()
так же, которая имеет приоритет надBaseClass
-версиямиclass
MyClass
Предоставляет свою собственную версиюSomeMethod()
- которая имеет приоритет надtrait
-версиями.Вывод
Interface
не может обеспечить реализацию по умолчанию тела метода, в то время какtrait
может.Interface
является полиморфной , унаследованной конструкцией, а atrait
- нет.Interface
s могут использоваться в одном классе, также как и несколькоtrait
s.источник
mixin
- и, поскольку я вновь обратился к открытию своего ответа, я обновился, чтобы отразить это. Спасибо за комментарий, @BoltClock!Я думаю
traits
, что полезно создавать классы, которые содержат методы, которые можно использовать как методы нескольких разных классов.Например:
Вы можете использовать и использовать этот метод «error» в любом классе, который использует эту черту.
При этом
interfaces
вы можете объявить только сигнатуру метода, но не код его функции. Также для использования интерфейса необходимо следовать иерархии, используяimplements
. Это не относится к чертам.Это совершенно другое!
источник
to_integer
было бы более вероятно включить вIntegerCast
интерфейс, потому что нет принципиально подобного способа (интеллектуального) приведения классов к целому числу.use Toolkit
вас может быть$this->toolkit = new Toolkit();
или я упускаю какую-то выгоду от самой черты?Something
контейнере вы делаетеif(!$something->do_something('foo')) var_dump($something->errors);
Для начинающих выше ответ может быть трудным, это самый простой способ понять это:
Черты
так что если вы хотите иметь
sayHello
функцию в других классах без повторного создания всей функции, вы можете использовать черты,Круто верно!
Не только функции, которые вы можете использовать что-либо в признаке (функция, переменные, const ..). Также вы можете использовать несколько черт:
use SayWorld,AnotherTraits;
Интерфейс
так вот как интерфейс отличается от черт: вы должны воссоздать все в интерфейсе в реализованном классе. Интерфейс не имеет реализации. и интерфейс может иметь только функции и const, он не может иметь переменных.
Надеюсь, это поможет!
источник
Это хороший способ думать об этом в большинстве случаев, но между ними есть ряд тонких различий.
Начнем с того, что
instanceof
оператор не будет работать с признаками (т. Е. Признак не является реальным объектом), поэтому вы не можете узнать, имеет ли класс определенную особенность (или посмотреть, имеют ли два других несвязанных класса признак ). Это то, что они имеют в виду, будучи конструкцией для повторного использования горизонтального кода.В PHP теперь есть функции, которые позволят вам получить список всех признаков, используемых классом, но наследование признаков означает, что вам нужно будет делать рекурсивные проверки, чтобы надежно проверить, имеет ли класс в какой-то момент конкретную характеристику (есть пример код на страницах PHP документации). Но да, это, конечно, не так просто и чисто, как instanceof, и IMHO это особенность, которая сделает PHP лучше.
Кроме того, абстрактные классы по-прежнему являются классами, поэтому они не решают проблемы повторного использования кода, связанного с множественным наследованием. Помните, что вы можете расширить только один класс (реальный или абстрактный), но реализовать несколько интерфейсов.
Я обнаружил, что черты и интерфейсы действительно хорошо использовать рука об руку для создания псевдо множественного наследования. Например:
Это означает, что вы можете использовать instanceof, чтобы определить, является ли конкретный объект Door ключевым или нет, вы знаете, что получите согласованный набор методов и т. Д., И весь код находится в одном месте во всех классах, которые используют KeyedTrait.
источник
Черты просто для повторного использования кода .
Интерфейс просто обеспечивает сигнатуру функций, которые должны быть определены в классе, где он может использоваться в зависимости от усмотрения программиста . Таким образом, давая нам прототип для группы классов .
Для справки - http://www.php.net/manual/en/language.oop5.traits.php
источник
В принципе, вы можете рассматривать черту как автоматическое «копирование-вставка» кода.
Использование черт опасно, так как нет смысла знать, что он делает до исполнения.
Тем не менее, признаки являются более гибкими из-за отсутствия таких ограничений, как наследование.
Черты могут быть полезны для внедрения метода, который проверяет что-то в классе, например, наличие другого метода или атрибута. Хорошая статья об этом (но по-французски, извините) .
Для тех, кто читает по-французски и может его получить, в журнале GNU / Linux HS 54 есть статья на эту тему.
источник
Если вы знаете английский и знаете, что это
trait
значит, это именно то, что говорит название. Это набор классов и свойств без классов, которые вы присоединяете к существующим классам путем вводаuse
.По сути, вы можете сравнить его с одной переменной. Функции замыкания могут
use
эти переменные вне области видимости, и таким образом они имеют значение внутри. Они мощные и могут быть использованы во всем. То же самое происходит с чертами, если они используются.источник
Другие ответы проделали большую работу по объяснению различий между интерфейсами и особенностями. Я остановлюсь на полезном примере из реального мира, в частности, который демонстрирует, что черты могут использовать переменные экземпляра, что позволяет вам добавлять поведение в класс с минимальным стандартным кодом.
Опять же, как уже упоминалось другими, черты хорошо сочетаются с интерфейсами, что позволяет интерфейсу указывать контракт поведения и черту для выполнения реализации.
Добавление возможностей публикации / подписки на события в класс может быть распространенным сценарием в некоторых основах кода. Есть 3 общих решения:
use
чертить, или импортировать их, чтобы получить возможности.Насколько хорошо работает каждый?
# 1 не работает хорошо Так будет до того дня, когда вы поймете, что не можете расширить базовый класс, потому что вы уже расширяете что-то еще. Я не буду показывать пример этого, потому что должно быть очевидно, насколько ограничено использование наследования, подобного этому.
# 2 и # 3 оба работают хорошо. Я покажу пример, который подчеркивает некоторые различия.
Во-первых, некоторый код, который будет одинаковым в обоих примерах:
Интерфейс
И немного кода для демонстрации использования:
Хорошо, теперь давайте покажем, как реализация
Auction
класса будет отличаться при использовании черт.Во-первых, вот как будет выглядеть # 2 (с использованием композиции):
Вот как будет выглядеть # 3 (черты):
Обратите внимание, что код внутри
EventEmitterTrait
точно такой же, как и внутриEventEmitter
класса, за исключением того, что признак объявляетtriggerEvent()
метод защищенным. Таким образом, единственное отличие, на которое вам нужно обратить внимание, - это реализацияAuction
класса .И разница большая. Используя композицию, мы получаем отличное решение, позволяющее нам повторно использовать наше
EventEmitter
количество классов, сколько нам нужно. Но основным недостатком является то, что у нас есть много шаблонного кода, который нам нужно написать и поддерживать, потому что для каждого метода, определенного вObservable
интерфейсе, нам нужно реализовать его и написать скучный шаблонный код, который просто перенаправляет аргументы в соответствующий метод в наш составленныйEventEmitter
объект. Использование этого признака в этом примере позволяет нам избежать этого , помогает сократить стандартный код и повысить удобство сопровождения .Однако могут быть случаи, когда вы не хотите, чтобы ваш
Auction
класс реализовывал полныйObservable
интерфейс - может быть, вы хотите предоставить только 1 или 2 метода, или, возможно, вообще ни одного, чтобы вы могли определить свои собственные сигнатуры методов. В таком случае вы все равно можете предпочесть метод композиции.Но эта черта очень привлекательна в большинстве сценариев, особенно если в интерфейсе много методов, что заставляет вас писать множество шаблонов.
* На самом деле вы могли бы сделать и то и другое - определить
EventEmitter
класс на тот случай, если вы когда-нибудь захотите использовать его композиционно, а также определитьEventEmitterTrait
черту, используяEventEmitter
реализацию класса внутри черты :)источник
Этот признак такой же, как класс, который мы можем использовать для множественного наследования, а также для повторного использования кода.
Мы можем использовать черты внутри класса, а также мы можем использовать несколько черт в одном классе с помощью ключевого слова use.
Интерфейс использует для повторного использования кода так же, как черта
Интерфейс расширяет несколько интерфейсов, поэтому мы можем решить проблемы множественного наследования, но когда мы реализуем интерфейс, мы должны создать все методы внутри класса. Для получения дополнительной информации нажмите ссылку ниже:
http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php
источник
Интерфейс - это контракт, в котором говорится, что «этот объект способен делать эту вещь», тогда как черта дает объекту возможность делать эту вещь.
Черта - это, по сути, способ «копировать и вставлять» код между классами.
Попробуйте прочитать эту статью. Что такое черты PHP?
источник
Основное отличие состоит в том, что с интерфейсами вы должны определить фактическую реализацию каждого метода в каждом классе, который реализует указанный интерфейс, так что вы можете иметь много классов, реализующих один и тот же интерфейс, но с другим поведением, в то время как признаки - это просто фрагменты кода, внедренные в класс; Другое важное отличие состоит в том, что методы trait могут быть только методами класса или static-методами, в отличие от методов интерфейса, которые также могут (и обычно являются) методами экземпляра.
источник