Я предполагаю, что нужно некоторое время изучать языки, которые имеют черты, чтобы изучить общепринятые хорошие/лучшие практики. Мое текущее мнение о 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;
}
Это приведет к ошибке (демонстрация). Точно так же любые методы, объявленные в трейте, которые также уже объявлены в используемом классе, не будут скопированы в класс, например.
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();
работает (демонстрация), но теперь черта тесно связана с A, и вся идея горизонтального повторного использования потерял.
Если вы будете следовать принципу разделения интерфейса, у вас будет много небольших классов и интерфейсов. Это делает Traits идеальным кандидатом для вещей, которые вы упомянули, например. сквозные задачи, но не для составления объектов (в структурном смысле). В приведенном выше примере Logger черта полностью изолирована. Он не имеет зависимостей от конкретных классов.
Для достижения тот же результирующий класс, но недостатком использования агрегации/композиции является то, что нам придется вручную добавлять методы прокси/делегатора в каждый класс, после чего он сможет регистрироваться. Черты прекрасно решают эту проблему, позволяя мне хранить шаблон в одном месте и выборочно применять его там, где это необходимо.
Примечание: трейты — это новая концепция PHP, поэтому все мнения, высказанные выше, могут быть изменены. У меня еще не было много времени, чтобы оценить концепцию самостоятельно. Но я надеюсь, что этого достаточно, чтобы дать вам пищу для размышлений.
25.10.2011