Acho que seria preciso procurar em idiomas que possuam Traits há algum tempo para aprender as Boas / Melhores práticas aceitas. Minha opinião atual sobre o Trait é que você deve usá-los apenas para códigos que precisaria duplicar em outras classes que compartilham a mesma funcionalidade.
Exemplo para uma característica do criador de logs:
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;
}
E então você faz ( demo )
$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);
Eu acho que o importante a considerar ao usar traços é que eles realmente são apenas pedaços de código que são copiados para a classe. Isso pode facilmente levar a conflitos, por exemplo, quando você tenta alterar a visibilidade dos métodos, por exemplo,
trait T {
protected function foo() {}
}
class A {
public function foo() {}
}
class B extends A
{
use T;
}
O exemplo acima resultará em um erro ( demo ). Da mesma forma, quaisquer métodos declarados na característica que também já estão declarados na classe using não serão copiados para a classe, por exemplo
trait T {
public function foo() {
return 1;
}
}
class A {
use T;
public function foo() {
return 2;
}
}
$a = new A;
echo $a->foo();
imprimirá 2 ( demo ). Essas são as coisas que você deseja evitar, pois dificultam a localização dos erros. Você também deve evitar colocar coisas em características que operam nas propriedades ou métodos da classe que a utiliza, por exemplo,
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();
funciona ( demo ), mas agora a característica está intimamente ligada a A e toda a idéia de reutilização horizontal é perdida.
Ao seguir o Princípio de Segregação de Interface, você terá muitas classes e interfaces pequenas. Isso faz da Traits um candidato ideal para as coisas que você mencionou, por exemplo , preocupações transversais , mas não para compor objetos (em um sentido estrutural). No exemplo do Logger acima, a característica é completamente isolada. Não possui dependências de classes concretas.
Poderíamos usar agregação / composição (como mostrado em outro lugar nesta página) para obter a mesma classe resultante, mas a desvantagem de usar agregação / composição é que teremos que adicionar os métodos proxy / delegador manualmente a cada classe, o que deve ser poder fazer logon. As características resolvem isso muito bem, permitindo-me manter o padrão em um só lugar e aplicá-lo seletivamente quando necessário.
Nota: dado que os traços são um novo conceito em PHP, todas as opiniões expressas acima estão sujeitas a alterações. Ainda não tive muito tempo para avaliar o conceito. Mas espero que seja bom o suficiente para lhe dar algo em que pensar.