Wydaje mi się, że przez jakiś czas należałoby przyjrzeć się językom, które mają cechy, aby poznać przyjęte dobre / najlepsze praktyki. Moja obecna opinia na temat Trait jest taka, że powinieneś używać ich tylko do kodu, który musiałbyś powielać w innych klasach, które mają tę samą funkcjonalność.
Przykład cechy Loggera:
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;
}
A potem robisz ( demo )
$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);
Myślę, że ważną rzeczą do rozważenia podczas używania cech jest to, że tak naprawdę są to tylko fragmenty kodu, które są kopiowane do klasy. Może to łatwo prowadzić do konfliktów, na przykład przy próbie zmiany widoczności metod, np
trait T {
protected function foo() {}
}
class A {
public function foo() {}
}
class B extends A
{
use T;
}
Powyższe spowoduje błąd ( demo ). Podobnie, żadne metody zadeklarowane w trait, które są już zadeklarowane w klasie using nie zostaną skopiowane do klasy, np.
trait T {
public function foo() {
return 1;
}
}
class A {
use T;
public function foo() {
return 2;
}
}
$a = new A;
echo $a->foo();
wypisze 2 ( demo ). Są to rzeczy, których będziesz chciał uniknąć, ponieważ utrudniają one znalezienie błędów. Będziesz także chciał uniknąć przypisywania rzeczy cechom operującym na właściwościach lub metodach klasy, która z nich korzysta, np
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();
działa ( wersja demonstracyjna ), ale teraz cecha jest ściśle powiązana z A i cała idea ponownego wykorzystania w poziomie zostaje utracona.
Postępując zgodnie z zasadą segregacji interfejsów , będziesz mieć wiele małych klas i interfejsów. To sprawia, że cechy są idealnym kandydatem do rzeczy, o których wspomniałeś, np. Przekrojów , ale nie do komponowania obiektów (w sensie strukturalnym). W powyższym przykładzie Loggera cecha jest całkowicie izolowana. Nie ma zależności od konkretnych klas.
Moglibyśmy użyć agregacji / kompozycji (jak pokazano w innym miejscu na tej stronie), aby osiągnąć tę samą wynikową klasę, ale wadą korzystania z agregacji / kompozycji jest to, że będziemy musieli ręcznie dodać metody proxy / delegatora do każdej klasy, a następnie powinno móc się logować. Cechy dobrze to rozwiązują, pozwalając mi trzymać szablon w jednym miejscu i selektywnie go stosować w razie potrzeby.
Uwaga: biorąc pod uwagę, że cechy są nową koncepcją w PHP, wszystkie opinie wyrażone powyżej mogą ulec zmianie. Nie miałem jeszcze zbyt wiele czasu, aby samemu ocenić koncepcję. Mam jednak nadzieję, że wystarczy, aby dać Ci coś do przemyślenia.