in php object-oriented ~ read.

Traits em PHP. Herança Horizontal

Post revisitado em junho/2014. Tudo que está aí funciona na versão atual PHP 5.5.x. Enjoy ;)

Antevejo que no futuro, o PHP como uma linguagem iterativa e incremental que é, adicionará ao seu núcleo um recurso muito valioso na categoria orientação à objetos.Antevejo ele, os Traits. E mais, consigo prever que será algo assim:

<?php
trait Hello {
     public function ola() {
           echo "Olá";
     }
}

class Mundo {
     use Hello;

    public function world() {
           echo "{$this->ola()} trait !";
  }
}

$ola = new Mundo();
$ola->world();
// Olá trait !

Ok, não sou bidu. Realmente os Traits estão na lista do possível-futuro PHP 5.4 e eu, baixei a build de 31/03/2011 para testar.

Traits

O PHP não suporta herença multipla – amém – e por este motivo, às vezes ficamos limitados em algumas decisões de projeto (design). O maior problema neste ponto é que toda herença até agora (PHP 5.3.x) é vertical, ou seja, se eu precisar de um nível de abstração diferente no meio do processo, ou precisarei adicionar a nova abstração e reescrever tudo abaixo dela, ou ainda, terei que duplicar o código pois não conseguirei satisfazer a herença.

É nesse ponto que os Traits aparecem. Diferentemente da herença (vertical), os Traits possibilitam-nos criar herenças horizontais.

Exemplo, dada a necessidade: haverão Funcionários, Supervisores, Gestores e Diretores no sistema e estes possui em comum o fato de receber salário. (capitalismo). Uma possibilidade seria:

Pessoa > Funcionario Pessoa > Supervisores Pessoa > Gestores Pessoa > Diretores

Tudo perfeito. Porém, chega outra necessidade: Gestores e Diretores tem a capacidade de demitir. Gestores precisam gerenciar cronogramas, assim como os Supervisores fazem.

Pessoa.demitir(pessoa) não faz sentido, pois afetiria todos. Gestor.demitir(pessoa) e Diretor.demitir(pessoa) atende, mas duplicaria o código.

Pessoa > Supervisor > Gestor Pessoa > Diretor

Shi, apareceu outro nível hierarquico. Traits auxilia exatamente neste ponto ! Vejamos:

Trait Cronograma

Trait Evil (que contém recursos para demitir uma pessoa)

Pessoa > Funcionario

Pessoa > Supervisor : Cronograma

Pessoa > Gestor : Cronograma : Evil

Pessoa > Diretor : Evil

Note que o Pessoa > Supervisor : Cronograma – os dois pontos para indicar o Trait são de minha autoria – era o Pessoa > Supervisor antigo. Isso porque as atribuições para gerenciar cronogramas foram passadas para o Trait.

Neste pequeno exemplo, o Trait nos auxiliou com um problema de domínio. Acredito que ele também aplique-se e muito bem para problemas onde temos domínios transversais, pois, nele ficariam as atividades do outro domínio pode o domínio principal teria acesso aos recursos sem quebrar a SRP.

Traits em PHP

Como disse no começo, o futuro-provável PHP 5.4 virá com o Trait. Por enquanto essa implementação funcionaria:

<?php
trait Cronograma {
     private static $ATRASADO = "sempre";

     public function cobrarDe(Pessoa $funcionario) {
           $funcionario->definirPressao(self::$ATRASADO);
           $this->youTube();
     }

     private function youTube() {

     }
}

class Funcionario extends Pessoa {
}

class Supervisor extends Pessoa {
     use Cronograma;
}

$colaborador = new Funcionario();
$controlador = new Supervisor();
$controlador->cobrarDe($colaborador);

Os Traits possuem boas capacidades com métodos de instância e estáticos. Vamos explorar um pouco mais:

<?php
trait Hello {
     public function sayHello() {
           echo "Olá";
     }
}

trait World {
     public function sayWorld() {
           echo "Mundo";
     }
}

class View {
     use Hello, World;
}

$view = new View();
$view->sayHello();
$view->sayWorld();
// exibe: Olá Mundo

Pegando informações da Classe

<?php
trait QuemSou {
     public function eu() {
           return get_class($this);
     }
}

class Foo {
     use QuemSou;
}

$foo = new Foo();
echo $foo->eu();
// exibe: Foo

Usando Variáveis de instância da classe:

<?php
trait World {
     public function help() {
           return "{$this->hello} world";
     }
}

class Hello {
     private $hello = "Hello";
     use World;
}

$hello = new Hello();
echo $hello->help();
// exibe: Hello world

Modificadores de acesso dos traits na classe

<?php
trait Hello {
     public function ola() {
           echo "Olá mundo";
     }
}

class Modificadores {
     use Hello { ola as private };
}

$ola = new Modificadores();
$ola->ola();
// Fatal Error....

Modificadores de acesso com alias para nome de método

<?php
trait Hello {
     private function ola() {
           echo "Olá mundo";
     }
}

class Modificadores {
     use Hello { ola as public aloha };
}

$ola = new Modificadores();
$ola->aloha();
// Olá mundo

Há outros vários recursos como métodos abstratos nos Traits, precedência, métodos estáticos, late static bindings, métodos mágicos, alias para nome de método do Trait ao usá-lo na classe. Para Rubistas ou Scalistas (?) as possibilidades são as mesmas dos Mixins e Traits de Ruby e Scala respectivamente.

RFC do PHP Trait PHP doc do Trait. PHP >= 5.4

comments powered by Disqus