Mirin webspace

Nejbohatší život má ten, kdo žije s minimem nároků

22. 5. 2008 PHP

Traity II

Minule jsem zhruba nastínil základní myšlenku traitů, dnes se zmíním trochu více o dalších vlastnostech traitů vzhledem k dědičnosti, problémům při konfliktech jmen a kompozici. Všechny informace jsem čerpal z rfc na php wiki. Jak jsem tak koukal, je tam i další rfc, které se týká traitů a jejich použití vzhledem k rozsahu definice traitu a třídy, která trait použije, to jsem zatím nějak podrobněji nezkoumal.

Zarovnání vlastností

Vícenásobní dědičnost a mixiny jsou mnohem komplexnější mechanizmy než traity. trait sám o sobě nemá žádný dopad do runtime a chování tříd. Trait je vsunut a zarovnán do tříd, které ho používají, lze ho také přirovnat k bezpečnému kopírování kusu kódu do vytvářených tříd.

Zachování hierarchie

Traity zachovají dědičnost při kompozici v třídách s traity. Veškerá kontrola je ponechána na vývojáři.

 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!

Zděděná metoda Base třídy je přetížena ve třídě MyHelloWorld metodou vloženou traitem. Přetěžovat můžeme samozřejmě i v samotné třídě MyHelloWorld, toto přetížení má pak nejvyšší prioritu.

 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!

Vícenásobné použití traitů a konflikty

Proč bychom nemohli použít více traitů? To by samozřejmě nevadilo, problém se pak může vyskytnout v případe, že traity obsahují stejné metody, pak dojde ke konfliktu.

 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;
 }

Vícenásobní dědičnost a mixiny si s tím poradí, trait ne. Jen vývojář má plnou kontrolu nad kompozicí výsledné třídy. PHP by mělo formou informovat, že došlo ke konfliktu metod smallTalk() a bigTalk() a nadále pokračovat jakoby tam žádný trait nebyl.

Vývojář by měl mít možnost specifikovat, jak konflikt řešit.

 class Talker {
   use A, B {
     B::smallTalk instead A::smallTalk,
     A::bigTalk instead B::bigTalk
   }
 }

Výše uvedené znamená vynechání smallTalk() z traitu a bigTalk() z traitu B. Protože samotné vyjmutí nemusí někdy stačit, bude možné i aliasovat použití traitu.

class Talker {
   use A, B {
     B::smallTalk instead A::smallTalk,
     A::bigTalk instead B::bigTalk,
     A::bigTalk as talk
   }
 }

Smysl je podobný jako u polí, výsledná třída Talker pak bude obsahovat

  • bigTalk() { echo 'A'; }
  • smallTalk() { echo 'b'; }
  • talk() { echo 'B'; }

Kompozice traitů

Protože traity jsou ve výsledku zarovnány do tříd, je možné traity skládat

 trait Hello {
   public function sayHello() {
     echo 'Hello ';
   }
 }
 
 trait World {
   public function sayWorld() {
     echo 'World!';
   }
 }
 
 trait HelloWorld {
   use Hello, World;
 }
 
 class MyHelloWorld {
   use HelloWorld;
 }
 
 $o = new MyHelloWorld();
 $o->sayHello();
 $o->sayWorld();
 // Results eventually in: Hello World!

Abstraktní metody jako vyjádření nutnosti implementace v traitu

Pokud potřebujeme vyjádřit nutnost přítomnosti implementace nějaké funkce, která je potřeba v traitu, jako nejvhodnější se k tomu hodí abstraktní metody.

 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;
   }
 }

Tato možnost je preferovanější oproti dynamickému vyhodnocování příslušných metod za běhu zejména u komplexnějších projektů.

RFC na php wiki zmiňuje ještě další aspekty jako nastavení viditelnosti (public, protected) u metod v traitech a detaily k aliasingu. Také se tam nacházejí patche pro aktuální větvě PHP (5.2,5.3). Traity jsou rozhodně zajímavá věc, uvidíme, zda se traity v PHP někdy objeví, nicméně v dohledném horizontu s tím nelze moc počítat.


Komentáře (0)

Komentáře jsou uzavřeny.