
Трейты в PHP существуют с версии 5.4, то есть относительно давно, они добавляют возможность переиспользования кода без наследования, когда один и тот же код можно использовать в совершенно не связанных между собой классах. Факт использования трейтов во фреймворке Symfony, говорит о том, что они могут без проблем вписаться в современные архитектурные решения кода и ничуть не устарели. В целом, они довольно успешно заменяли классическую "копи-пасту", пока, вместе с развитием языка PHP, к трейтам не стало всё больше и больше претензий в ракурсе ООП. О недостатках упоминали и раньше, сразу же после выхода новинки.
Основной недостаток был заявлен в том, что при наследовании можно легко отследить код на уровень вверх и вниз и понять, что происходит. С трейтами этот поиск расширяется "по-горизонтали", а так как трейты можно вставлять в трейты, то будет трудно определить все взаимосвязи, а для программиста нужно не только разобраться, но понять и при необходимости изменить. Поэтому вкладывать трейты в трейты можно считать таким же плохим тоном, как большое количество уровней наследования.
Также трейты в PHP могут быть использованы только внутри класса, при этом они могут работать (читать и писать) с внутренним состоянием этого класса, в который подмешаны. В отличие от класса в трейте нельзя использовать константы.
Подробное определение (взято из Википедии):
Трейт - это механизм обеспечения повторного использования кода в языках с поддержкой только одиночного наследования, таких как PHP. Трейт предназначен для уменьшения некоторых ограничений одиночного наследования, позволяя разработчику повторно использовать наборы методов свободно, в нескольких независимых классах и реализованных с использованием разных архитектур построения классов.
Да, в PHP наследовать можно только от одного класса. Если вы выстроили две независимые ветви наследования, а участок кода в них всё равно повторяется, то здесь можно использовать трейт. Также многие используют трейты для "нарезки" класса на части (рекомендую в этом случае делать только приватные методы трейта, а публичные - только в классе). Это самое невинное использование трейта и врятли кто-то будет против. Вы разделили класс на логические составляющие и они лежат где-то рядом с классом в отдельной папке. Но всё же, если речь про переиспользование кода, то правильнее использовать трейты по прямому назначению, но в рамках одного модуля. В общем проекте это будет дублирование кода (трейта), если понадобится в другом модуле. Но код меняется и эти части вполне могут стать непохожими через некоторое время.
По архитектуре приложения можно вести бесконечные дебаты, уточню только, что работал в довольно известной компании, где трейты активно использовались и это было реализовано красиво, но сразу же на следующей работе в требованиях к разработке трейты были заклеймлены как зло. Во второй компании более активно использовалось тестирование, надо заметить, возможно поэтому был столь безоговорочный отказ от них. У меня нет претензий к компетенции тех или других команд разработки, но они по-своему справлялись, как с трейтами, так и без, но читателю этих строк будет понятно, почему эта тема показалась мне интересной.
Трейты есть не только в PHP, например в Rust, где нет классического наследования "родитель-потомок", вместо этого используют трейты (и нечто ещё, называемое "супертрейт"). Трейты есть и в других языках, Scalа, например, в других просто названы по-другому, в C# называются "интерфейсы с реализацией методов по умолчанию".
Интересно, что в PHP трейты могут иметь свойства, при этом в классе не должно быть свойства с таким же именем. Это добавляет ответственности к именованию не только методов, но и свойств, они могут непредсказуемо пересекаться с названиями методов в других трейтах в классе или из самого класса. Как минимум они (названия) не должны быть односложными. В крайне запутанных случаях можно использовать insteadof и as для разрешения таких конфликтов. Минусом трейта может быть невозможность инкапсулировать часть его методов и свойств, даже если они приватные.
Чтобы уменьшить "непредсказуемое" влияние трейтов на проект, можно назначать каждому интерфейс (это вполне отвечает принципу разделения интерфейсов), пользоваться правилом "один трейт - один метод" и делать трейты самодостаточными, то есть, все что трейт использует должно быть в нём же и объявлено.
Простой пример использования трейта.
<?php
class A {
use BTrait;
public function getValue() {
return $this->value;
}
}
trait BTrait {
private $value = 48;
public function getTraitValue() {
return $this->value;
}
}
$obj = new A();
print $obj->getValue(); // 48
print $obj->getTraitValue(); // 48
В первом абзаце статьи упоминается, что к трейтам есть претензии с точки зрения ООП. Ну конечно, иначе бы ООП называлось Объектно-Трейтовое-Программирование. Естественно, общие практики программирования складывались без конкретных отхождений каждого из языков, таких как трейты в PHP, поэтому общеизвестные паттерны используют что угодно, наследование, композицию, но только не трейты. Так как во всеохватывающих практиках делается попытка объяснить всё, то это "всё" на выходе без ньюансов и дополнительных возможностей языков программирования. В этом нет проблемы, но объясняет расхождение во взглядах у вполне продвинутых программистов, в ином случае, если "не вполне", то написать неподдерживаемый код можно используя что угодно, сам инструмент в этом не виноват.