Трейты и интерфейсы php

Трейты в PHP

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

Трейты решают проблему дублирования кода.

Давайте рассмотрим пример использования трейта.

Важно понимать то, что трейты не присваивают экземплярам класса (объектам) новый тип.

Невозможно создать самостоятельный экземпляр трейта.

Использование нескольких трейтов

С этим моментом всё просто.

Совместное использование трейтов и интерфейсов

Конфликты имён в трейтах, insteadof.

Давайте на примере рассмотрим как это работает:

Псевдонимы для переопределённых методов

Вот как это работает:

Статические методы в трейте

Нам ничего не мешает объявлять методы в трейте статическими. Давайте рассмотрим пример трейта со статическими методами.

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

Доступ к свойствам базового класса

Абстрактные методы в классах

Если в трейте объявляется абстрактный метод, то этот метод должен быть реализован в базовом классе.

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

Изменения прав доступа к методам трейта

Очевидно, что внутри трейта мы можем использовать любой любой модификатор доступа ( public, private, protected ) для метода. Но, кроме этого, у нас есть возможность в классе менять этот модификатор на другой. Для этого в оператор use после слова as можно указать новый модификатор.

Итоги

Кратко о том, что мы выучили.

Источник

Трейты в PHP

PHP поддерживает только одиночное наследование: дочерний класс не может наследовать от нескольких классов сразу, только от одного единственного родителя. Однако в большинстве случаев было бы полезно наследовать от нескольких классов. Например, было бы желательно наследовать методы от нескольких разных классов, чтобы предотвратить дублирование кода. Трейты используются для восполнения этого пробела, позволяя нам повторно использовать одни и те же свойства и методы в нескольких классах.

Что такое трейты?

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

Синтаксис трейта такой же как и у класса, за исключением того, что имя трейта нужно объявлять с помощью ключевого слова trait :

Синтаксис

Экземпляр трейта, как и абстрактного класса, нельзя создать — трейты предназначены только для подключения к другим классам.

Синтаксис

Давайте посмотрим на пример в котором объявим один трейт и подключим к классу:

Пример

Результат выполнения кода:

По сути, трейт — это просто способ скопировать и вставить код во время выполнения.

Использование нескольких трейтов

Пример

Результат выполнения кода:

Чем трейты отличаются от интерфейсов?

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

Интерфейс — это контракт, в котором говорится, что «этот объект может делать это», тогда как трейт дает объекту возможность делать это.

Другими словами, если код ООП касается планирования и проектирования, то интерфейс — это план, а объект — полностью построенный дом. Между тем, трейты — это просто способ помочь построить дом, спроектированный по плану (интерфейсу).

Интерфейсы — это спецификации, которые можно проверить используя оператор instanceof (является ли текущий объект экземпляром указанного класса).

Вы должны использовать трейты только тогда, когда несколько классов имеют одну и ту же функциональность (вероятно, продиктованную одним и тем же интерфейсом). Нет смысла использовать трейт для обеспечения функциональности для одного класса: это только запутывает то, что делает класс.

Пример

Результат выполнения кода:

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

Читайте также:  объявление на медовый спас

Переопределение унаследованных методов

Как описано в Руководстве: Унаследованный метод от базового класса переопределяется методом, вставленным с помощью трейта. Порядок приоритета таков — методы текущего класса переопределяют методы трейта, которые в свою очередь переопределяют унаследованные методы.

Итак, рассмотрим следующий сценарий:

Пример

Результат выполнения кода:

При создании экземпляра MyClass, описанного выше, происходит следующее:

Заключение

Источник

Прочитал про новую штуку в php trait

Сделал первый пример

Работа понятна, вопрос чем отличается вышепоказанный пример, от

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

При таком подходе вы всегда класс логгера можете заменить, просто отредактировав всего один файл трейта, а не 100500 классов.

Еще пример:
В трейт можно вынести функцию isAjaxPost, для проверки, что запрос в контроллер пришел ajax post, и подключать в нужные контроллеры.

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

@Fesor всмысле когда, например модуль Блог хочет написать личное сообщение пользователю (Тесная связь модуля Блог и модуля Сообщения).

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

@Fesor Не соглашусь насчет макроса и того, что код вставляется в место use TraitName, в PHP это реализовано, почти так же, как extend, за исключением приоритетов, кто кого перекрывает. ну и да одноименные функции из трейтов друг друга перекрыть не могут, будет ошибка, но есть механизм выбора.

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

@nepster09 Сходу так прям не ответить, всего скорей это не удачное решение, т.к получаем связь блога и сообщений, с другой стороны если система не сложна, то вполне себе решение.

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

trait UserModuleTrait
<
protected function getUserModule() < // логика создания >
>

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

Но тут получается такая страшная штука, если условия отправления сообщений сменить с 10, на к примеру каждого 3 отправленное в субботу утром, то получается постоянно лезть в модель блога для исправления.

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

Хотя еще раз повторюсь, если проект не сложный, то первый вариант вполне себе рабочее решение 🙂

Источник

Трейты в PHP

PHP поддерживает только одиночное наследование: дочерний класс не может наследовать от нескольких классов сразу, только от одного единственного родителя. Однако в большинстве случаев было бы полезно наследовать от нескольких классов. Например, было бы желательно наследовать методы от нескольких разных классов, чтобы предотвратить дублирование кода. Трейты используются для восполнения этого пробела, позволяя нам повторно использовать одни и те же свойства и методы в нескольких классах.

Что такое трейты?

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

Синтаксис трейта такой же как и у класса, за исключением того, что имя трейта нужно объявлять с помощью ключевого слова trait :

Синтаксис

Экземпляр трейта, как и абстрактного класса, нельзя создать — трейты предназначены только для подключения к другим классам.

Синтаксис

Давайте посмотрим на пример в котором объявим один трейт и подключим к классу:

Пример

Результат выполнения кода:

По сути, трейт — это просто способ скопировать и вставить код во время выполнения.

Использование нескольких трейтов

Пример

Результат выполнения кода:

Чем трейты отличаются от интерфейсов?

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

Читайте также:  мем что ж неудобно вышло

Интерфейс — это контракт, в котором говорится, что «этот объект может делать это», тогда как трейт дает объекту возможность делать это.

Другими словами, если код ООП касается планирования и проектирования, то интерфейс — это план, а объект — полностью построенный дом. Между тем, трейты — это просто способ помочь построить дом, спроектированный по плану (интерфейсу).

Интерфейсы — это спецификации, которые можно проверить используя оператор instanceof (является ли текущий объект экземпляром указанного класса).

Вы должны использовать трейты только тогда, когда несколько классов имеют одну и ту же функциональность (вероятно, продиктованную одним и тем же интерфейсом). Нет смысла использовать трейт для обеспечения функциональности для одного класса: это только запутывает то, что делает класс.

Пример

Результат выполнения кода:

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

Переопределение унаследованных методов

Как описано в Руководстве: Унаследованный метод от базового класса переопределяется методом, вставленным с помощью трейта. Порядок приоритета таков — методы текущего класса переопределяют методы трейта, которые в свою очередь переопределяют унаследованные методы.

Итак, рассмотрим следующий сценарий:

Пример

Результат выполнения кода:

При создании экземпляра MyClass, описанного выше, происходит следующее:

Заключение

Источник

Как я использую трейты

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

Воздействие vs Абстракция

Первое, что вы должны сделать — пойти почитать пост “Abstraction or Leverage” от Майкла Найгарда. Это отличная статья.

Если же у вас мало времени, основная суть поста состоит в том, что части кода (функции, классы, методы и т.д.) могут предназначаться либо для абстракции, либо для воздействия. Разница в:

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

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

Как это связано с трейтами?

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

Наследование, с другой стороны, великолепно в предоставлении воздействия. Оно позволяет нам переопределить части родительского кода без необходимости копировать или извлекать каждый метод, использовать код класса (но не обязательно абстрактного) несколько раз. Так, отвечая на первоначальный вопрос, когда же я могу использовать трейты?

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

Иногда?

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

Но все же статические методы полезны. Если у вас одна функция без состояния и вы не хотите заменить ее на другую реализацию, то нет ничего плохого в том, чтобы сделать ее статической. Именованные конструкторы (вы же редко хотите именно пустой объект) или получение массива/результата математических операций с хорошо определенными вводом/выводом, без состояния, детерминированные: все это вам интересно. Статическое состояние, а не методы, вот реальное зло.

Трейты имеют примерно те же ограничения, плюс они могут быть использовании только внутри класса. Они более глобальны, чем объект.

Читайте также:  работа на шахтах кемеровской области с обучением

Это дает трейтам дополнительную особенность: они могут работать (читать и писать) с внутренним состоянием класса, в который подмешаны. В некоторых случаях это делает их более подходящими чем статические методы.

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

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

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

Итак, трейты являются хорошей заменой в подобных случаях, но это не значит, что статические методы бесполезны. На самом деле, я все же предпочитаю использовать статические методы в случаях, когда не нужно изменять внутреннее состояние объекта, поскольку это всегда безопасней. Статические методы также намного удобнее в тестировании, не требуют mock класса.

Создание утверждений является хорошим примером тех случаев, где я предпочитаю статические методы, несмотря на то, что их обычно можно поместить в трейты. Я нахожу, что Assertion::positiveNumber($int) дает мне вышеупомянутые преимущества и мне легче понять что делает вызываемый класс.

Так что заявляю: Я использую трейты, когда я хочу воздействия, которому требуется доступ к внутреннему состоянию объекта.

Родительские классы

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

При прочих равных, я бы ориентировался на правило вроде «Является-A против Имеет-A». Конечно, это не точное правило, потому что трейты не являются композицией, но разумный ориентир.

Другими словами, родительские классы нужно использовать для функций, которые присуще какому-то объекту. Родительские классы хорошо передают другим разработчикам смысл кода: «Сотрудник — это человек». Если нам необходимо воздействие, это не означает, что код не должен быть коммуникативным.

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

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

Интерфейсы

Я редко (если вообще) расширяю класс или создаю трейт без сопутствующего создания интерфейса.

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

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

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

Когда я не использую трейты

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

Есть несколько мест, где я не люблю использовать трейты из-за стилевых предпочтений:

Источник

Образовательный портал