Я полагаю, что в течение некоторого времени нужно искать языки, которые имеют черты, чтобы выучить принятые хорошие / лучшие практики. Мое текущее мнение о Trait состоит в том, что вы должны использовать их только для кода, который вам придется дублировать в других классах, которые имеют те же функции.
Пример для черты Logger:
interface Logger
{
public function log($message, $level);
}
class DemoLogger implements Logger
{
public function log($message, $level)
{
echo "Logged message: $message with level $level", PHP_EOL;
}
}
trait Loggable // implements Logger
{
protected $logger;
public function setLogger(Logger $logger)
{
$this->logger = $logger;
}
public function log($message, $level)
{
$this->logger->log($message, $level);
}
}
class Foo implements Logger
{
use Loggable;
}
И тогда вы делаете ( демо )
$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);
Я предполагаю, что при использовании черт важно учитывать, что они на самом деле являются просто частями кода, которые копируются в класс. Это может легко привести к конфликтам, например, когда вы пытаетесь изменить видимость методов, например
trait T {
protected function foo() {}
}
class A {
public function foo() {}
}
class B extends A
{
use T;
}
Вышеуказанное приведет к ошибке ( демо ). Аналогично, любые методы, объявленные в признаке, которые также уже объявлены в классе using, не будут скопированы в класс, например
trait T {
public function foo() {
return 1;
}
}
class A {
use T;
public function foo() {
return 2;
}
}
$a = new A;
echo $a->foo();
напечатает 2 ( демо ). Это те вещи, которые вы захотите избежать, поскольку они затрудняют поиск ошибок. Вы также захотите избегать помещения вещей в свойства, которые оперируют свойствами или методами класса, который его использует, например
class A
{
use T;
protected $prop = 1;
protected function getProp() {
return $this->prop;
}
}
trait T
{
public function foo()
{
return $this->getProp();
}
}
$a = new A;
echo $a->foo();
работает ( демо ), но теперь черта тесно связана с А, и вся идея горизонтального повторного использования теряется.
Когда вы будете следовать принципу разделения интерфейсов, у вас будет много небольших классов и интерфейсов. Это делает Traits идеальным кандидатом для упомянутых вами вещей, например сквозных задач , но не для создания объектов (в структурном смысле). В приведенном выше примере с Logger эта черта полностью изолирована. У него нет зависимости от конкретных классов.
Мы могли бы использовать агрегацию / композицию (как показано в другом месте на этой странице) для достижения того же результирующего класса, но недостатком использования агрегации / композиции является то, что нам придется вручную добавлять методы прокси / делегатора к каждому классу, тогда это должно быть в состоянии войти. Черты решают эту проблему, позволяя мне хранить шаблон в одном месте и выборочно применять его там, где это необходимо.
Примечание: учитывая, что черты являются новой концепцией в PHP, все высказанные выше мнения могут быть изменены. У меня еще не было времени самому оценить концепцию. Но я надеюсь, что это достаточно хорошо, чтобы дать вам о чем подумать.