Введение в Traits
Traits - это механизм повторного использования кода. Был разработан как альтернатива подмешиванию (mixin) и, в некотором роде, множественному наследованию, так как позволяет разработчику более свободно повторно использовать методы объявленные в иной иерархии классов. Семантика комбинирования traits и классов специально разработана для избежания типичных проблем связанных с множественным наследованием и технологией mixin. На момент написания статьи, данная технология находиться в trunk и будет включена в новые сборки dev версии PHP 6 (также возможно и в 5.3.x)
Пример использования
trait Speaker {
public function sayHello() {
echo 'Hello World!';
}
}
class Base {
use Speaker;
}
$test = new Base();
$test -> sayHello();
Возможно множественное использование trains, тогда запись приобретет вид:
class Base {
use Speaker, foo, bar;
}
Иерархия наследования
traints наследуется после родительского класса, и как следствие, может его перегружать, однако методы объявленные непосредственно в классе, перегрузят traints
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello(); // echos Hello World!
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
}
$o = new TheWorldIsNotEnough();
$o->sayHello(); // echos Hello Universe!
Разрешение конфликтов
traits уже используется во многих языках программирования, и, как показывает практика, конфликты случаются. Во многих системах попавшими под исследования разработчиков php, механизмы урегулирования конфликта попадались редко. Вот один из типичных примеров конфликта:
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}
class Talker {
use A, B;
}
Оба trait'а реализуют функции с одинаковыми именами. Для решения этой проблемы предлагается такой механизм:
class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
}
// or
class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk;
}
}
Traits состоящие из traits
Traits могут состоять из других traits
trait Hello {
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayWorld() {
echo 'World!';
}
}
trait HelloWorld {
use Hello, World;
}
class MyHelloWorld {
use HelloWorld;
}
Traits могут потребовать реализацию абстрактного метода
trait Hello {
public function sayHelloWorld() {
echo 'Hello'.$this->getWorld();
}
abstract public function getWorld();
}
class MyHelloWorld {
private $world;
use Hello;
public function getWorld() {
return $this->world;
}
public function setWorld($val) {
$this->world = $val;
}
}
Статические переменные
Статические переменные в методах имеют различную область видимости для всех различных классов использующих traits
trait Counter {
public function inc() {
static $c = 0;
$c = $c + 1;
echo "$c\n";
}
}
class C1 {
use Counter;
}
class C2 {
use Counter;
}
$o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
Область видимости
Изменение видимости методов traits в контексте составного класса возможно, хотя находиться еще в обсуждении
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
class MyClass1 {
use HelloWorld { sayHello as protected }
}
class MyClass2 {
use HelloWorld { doHelloWorld as private sayHello }
}
Итоги
- Traits не предназначены и не предоставляют возможность для динамического видоизменения структуры класса
- Traits интегрированы в приоритетную сортировку перегрузки методов
- Traits предоставляет механизм решения конфликтов
- Разработчик может назначить псевдоним для метода и выбрать, в случае конфликта, какой из методов использовать
С более подробным описанием, а также не вошедшими в текущую ветвь разработок возможностями вы можете ознакомиться здесь