Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Este conceito de traits me confunde muito, pois em vez de usar uma trait eu poderia muito bem fazer uma composição ou agregação de uma outra classe, e implementá-la na classe que desejo utilizar tais comportamentos.
Pois bem ainda não entendi a utilização dessa traits? Alguém poderia me explicar o beneficio de usar traits em vez de fazer uma composição ou agregação?
Obrigado grabriel pela paciência e pelo os exemplos citados, ficou show.. me ajudou bastante aqui
Só umas dúvidas:
1) Você citou no exemplo do Singleton a seguinte frase:
Outro ponto importante, é que uma classe Singleton não atua muito bem com herança (na realidade, ela inibe o uso da herança por trazer para si o controle da instância). Dessa forma, não é interessante implementar o Singleton como uma classe abstrata.
O que seria esse "inibe o uso da herança por trazer para si o controle da instância"?
Fiz os testes aqui com o Registry extendendo uma classe Singleton, e deu tudo certo...
2) Pelo que eu entendi do exemplo herança múltipla, as classes 'GUI/HTML' são uma especialização do 'GUI', por conta disso 'GUI/HTML' reaproveita todos os comportamento de 'GUI'. E como o php não existe herança múltipla, foi usado a Traits para estender comportamentos especifico para 'GUI/HTML'.. Correto o meu entendimento??
3) Qual o propósito do atributo '$leaf = false' na classe 'AbstractComponent.php' e 'AbstractLeaf.php'? Dos exemplos de herança múltipla
>
O que seria esse "inibe o uso da herança por trazer para si o controle da instância"?
Fiz os testes aqui com o Registry extendendo uma classe Singleton, e deu tudo certo...
Então, estou com o livro dos padrões GoF em mãos aqui, e existe a referência sobre subclasses.
Mas duas situações não poderiam ser reutilizadas (uma delas pode ser no PHP).
A propriedade $instance:
/**
* @var $this
**/
protected static $instance;
Pois como é o estado global da classe, o atributo deve ficar na especialização e não na abstração. Se ficar na abstração, teria que ser criada uma maneira de controlar o tipo de classe, a instância. Ou seja, mais implementação do que seria necessário com traits.
O outro seria a questão do construtor, pois os exemplos são em Smaltalk. Essa situação do construtor não ocorre no PHP somente por causa da implementação/comportamento conhecido como Late Static Binding. Mesmo essa situação não ocorrendo, é interessante entendê-la.
Traduzindo em miúdos, é poder criar a instância utilizando o "new static()". Pois o "new self()" criaria apenas a instância da classe aonde está implementada o método getInstance().
Em outras linguagens, não há métodos mágicos para isso, você deve utilizar o nome da classe que implementa o método getInstance().
Digamos que eu utilizaria o Singleton como uma classe abstrata, a reutilização de código não seria completa (não ao nível de traits).
Seria a abstract class Singleton sem o método getInstance(), sem a propriedade $instance e com o construtor protected (o que não é um problema):
abstract class Singleton
{
/**
* Final private constructor to prevent creating a new instance of the
Singleton* via the new operator from outside of this class
* and for not extend override the method
*/
protected function __construct()
{
}
/**
* Private unserialize method to prevent unserializing of the *Singleton*
* instance.
*
* @return void
*/
private function __wakeup()
{
}
/**
* Private clone method to prevent cloning of the instance of the
* *Singleton* instance.
*
* @return void
*/
private function __clone()
{
}
}
E a implementação de Registry possuiria o método getInstance();
class Registry extends Singleton
{
/**
* @var $this
**/
protected static $instance;
public static function getInstance()
{
return isset(static::$instance) ? static::$instance : static::$instance = new Registry();
}
}
Conclusão, é possível utilizar herança, mas com uma implementação maior e talvez mais burocrática.
2) Pelo que eu entendi do exemplo herança múltipla, as classes 'GUI/HTML' são uma especialização do 'GUI', por conta disso 'GUI/HTML' reaproveita todos os comportamento de 'GUI'. E como o php não existe herança múltipla, foi usado a Traits para estender comportamentos especifico para 'GUI/HTML'.. Correto o meu entendimento??
Exatamente. Sem o benefício das trais, teria que repetir o código ou perder algumas assinatura (assinaturas providas por herança) ou, até, mudar toda a estratégia que a implementação de herança múltipla contempla de forma mais simplista.
Com as traits, é possível utilizar interfaces em conjunto, mantendo o código sem repetição e as assinaturas de acordo.
3) Qual o propósito do atributo '$leaf = false' na classe 'AbstractComponent.php' e 'AbstractLeaf.php'? Dos exemplos de herança múltipla
Ele é pertinente ao padrão Composite, indica se um Component pode ou não ser pai de algum elemento.
Basicamente, o padrão Composite permite aos componentes ignorar suas diferenças e serem tratados de maneira uniforme. É basicamente uma estrutura em árvore, aonde existem os nós e os elementos finais (Leaf = Folha).
Ou seja, os elementos finais (Leafs) não podem receber outros elementos, mas os elementos do tipo Composite, por outro lado, podem.
Esse será um post cansativo e bem extenso, mas acho que valerá a pena.
Reutilização de código (de forma horizontal) através do copy and paste do interpretador. Dessa forma, pode prover, "uma certa", herança múltipla
Se você puder usar agregação e composição, deve utilizá-lo. Entretanto, existem momentos que isso não é viável ou existe a necessidade de um controle que somente a herança possui.
Eu lhe darei dois exemplos, um rápido e simples, outro extenso e complexo.
O 1º exemplo não é sobre herança, mas sobre reutilização de código.
Singleton
Singleton é um padrão de projeto que define que uma classe deve possuir apenas uma instância e um ponto de acesso global.
Normalmente, o padrão, é muito mal utilizado. Mas, sempre há a sua aplicabilidade. Um dos mais famosos uso é o Registry.
Registry é um repositórios de objetos. Normalmente ele é aplicado como um padrão Singleton (mas não necessariamente deve ser um).
Dessa forma, vamos a implementação de um Registry:
class Registry
{
/**
Beleza, vamos a análise.
O Registry, nesse exemplo, é um Singleton. Os seguintes pontos são específicos do Singleton:
Já os demais métodos, são específicos do Registry:
Suponhamos que eu precise de outra classe que implemente o Singleton (um manipulador para o DOM, por exemplo), logo, eu preciso implementar todos os métodos básicos novamente. Vamos ao molde da nossa próxima classe Singleton:
class SingletonClass
{
/**
Pronto, o código acima exemplifica o básico para uma classe Singleton. Logo, todas as classes que eu precisar de Singleton, terei de copiar e colar o código acima.
Como é de praxe, sabemos que copiar e colar não é uma boa alternativa e, nesse caso, devemos encontrar uma maneira para que o código seja reutilizado.
Outro ponto importante, é que uma classe Singleton não atua muito bem com herança (na realidade, ela inibe o uso da herança por trazer para si o controle da instância). Dessa forma, não é interessante implementar o Singleton como uma classe abstrata.
Eis que surgem as traits.
Nesse caso, você pode separar, em uma trait, toda a implementação do Singleton.
namespace Harbinger\StandardLibrary\Traits;
/**
* Trait for singleton
* @package Harbinger
* @subpackage StandardLibrary\Trait
* @author Gabriel Heming <gabriel.heming@hotmail.com>
**/
trait Singleton
{
As única diferenças são a adição da keyword final, para a classe que implementar o Singleton não sobrescreva o método implementado, e a implementação do método init, que pode ser usado como construtor de uma classe.
Dessa forma, a classe Registry pode ser reescrita da seguinte maneira:
namespace Harbinger\StandardLibrary;
/**
* Implementation of a data registry.
* @package Harbinger
* @subpackage StandardLibrary
* @author Gabriel Heming <gabriel.heming@hotmail.com.br>
**/
class Registry
{
Assim, foi utilizada uma trait (que realiza o copy and paste) para evitar o copy and paste de códigos através de vários arquivos.
Os exemplos acima você pode encontrar no pacote linkado abaixo:
https://bitbucket.org/harbingerproject/standard-library/overview
Herança múltipla
Como já foi mencionado, um dos exemplos de traits é prover a herança múltipla. Ou seja, quando um objeto precisa se comportar de acordo com outros dois.
Vamos para um diagrama de classes simples implementando o padrão de projeto Composite (para saber um pouco mais sobre composite, temos aqui, aqui e aqui).
O problema é simples, uma biblioteca reutilizável para criar interfaces gráficas. Coisa simples, mas que seja com baixo acoplamento e alta coesão.
Ps.: a implementação é extremamente longa (para chegar aonde eu quero), dessa forma, vou cortar muita coisa e ser bem sucinto.
Primeiro, ao diagrama:
A interface básica para o composite, logo, vamos para a implementação:
Component.php
namespace Harbinger\GUI;
/**
* Defines an interface for GUI component
* @package Harbinger
* @subpackage GUI
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
interface Component {
/**
Composite.php
namespace Harbinger\GUI;
/**
* @package Harbinger
* @subpackage GUI
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
interface Composite extends Component {
/**
* @param \Harbinger\GUI\Component $compoenent
Leaf.php
namespace Harbinger\GUI;
/**
* @package Harbinger
* @subpackage GUI
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
interface Leaf extends Component {
}
Expandido o diagrama, será criada a implementação para uma view básica:
As classes abstratas irão implementar o código em comum para todas as interfaces. Nesse caso, bora para a implementação:
AbstractComponent.php
namespace Harbinger\GUI;
/**
* @package Harbinger
* @subpackage GUI
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
abstract class AbstractComponent implements Component {
/**
* @var boolean
**/
AbstractComposite.php
namespace Harbinger\GUI;
/**
* @package Harbinger
* @subpackage GUI
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
abstract class AbstractComposite extends AbstractComponent implements Composite {
/**
* @var Component[]
**/
AbstractLeft.php
namespace Harbinger\GUI;
/**
* @package Harbinger
* @subpackage GUI
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
abstract class AbstractLeaf extends AbstractComponent implements Leaf {
/**
* {@inheritsdoc}
**/
Códigos base prontos, vamos a implementação de dois elementos apenas, o View e o Text. O view é um elemento Pai do tipo composite, ele nunca pode ser filho, apenas pode conter filhos. Já o Text, é um elemento do tipo Leaf.
Vamos a implementação:
View.php
namespace Harbinger\GUI;
/**
* @package Harbinger
* @subpackage GUI
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
class View extends AbstractComposite {
/**
* {@inheritsdoc}
**/
Text.php
namespace Harbinger\GUI;
/**
* @package Harbinger
* @subpackage GUI
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
class Text extends AbstractLeaf {
/**
* @var mixed
**/
E o uso:
Saída:
Texto 2Até o momento, nenhuma novidade. Tudo resolvido através do básico.
O que foi desenvolvido até o momento não chega bem a ser uma biblioteca básica. Entretanto, o que está implementado até aqui, serve de base para outros tipos de view.
E se eu quiser implementar para uma GUI html. É possível, mas, com algumas restrições.
Vamos as definições:
Até agora, tudo igual.
Nesse momento, chegam os pontos que só fazem sentido para o HTML:
Visto isso, terá de ser implementada as funcionalidades existentes do Composite e novas funcionalidades.
Fazendo uma análise da implementação, podemos facilmente verificar uma, de muitas, situações que não podem ser resolvidas com a herança simples.
Todas as classes abstratas (AbstractComponent, AbstractComposite e AbstractLeaf) deverão ser especializadas (extendidas) para possuir o novo comportamento. Até ai tudo bem. Todas as classes possuirão a assinatura necessária (Composite, Leaf, etc..) e não haverá duplicação de código
Nesse caso, a nova HTML\AbstractComponent deve extender de AbstractComponent. Já HTML\AbstractComposite e HTML\AbstractLeaf, além de extender HTML\AbstractComponent, também devem extender, respectivamente, AbstractComposite e AbstractLeaf. Pois, além de serem especializações de HTML\AbstractComponente, também são especializações de AbstractComposite e AbstractLeaf.
Outro ponto é a tag HTML (HTML\View) que não pode ser filha. Logo, ela deve, além de extender a nova classe HTML\AbstractComposite, extender a já existente View, pois tal como a view, ela não pode ser filha.
No PHP, não há herança múltipla, logo, isso não pode ser realizado. É nesse momento que a classe HTML\View perderá uma assinatura de implementação ou algum código será duplicado... O que é algo que não seria muito interessante
Veja como seria o diagrama de classes adequado em uma linguagem de programação que permite herança múltipla:
Agora que a p***a ficou louca... Vamos ao pontos principais:
Esse diagrama de classes não pode ser implementado dessa forma em PHP, pois PHP não possui herança múltipla.
Entretanto, com algumas modificações, ele pode ser implementado utilizando-se de traits.
Veremos as modificações:
Modificações:
Chega de explicações, mais implementações:
HTML\Component.php
namespace Harbinger\GUI\HTML;
/**
* Defines an interface for GUI component
* @package Harbinger
* @subpackage GUI\HTML
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
interface Component extends \Harbinger\GUI\Component {
/**
* @param string $class
HTML\Composite.php
namespace Harbinger\GUI\HTML;
/**
* @package Harbinger
* @subpackage GUI\HTML
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
interface Composite extends \Harbinger\GUI\HTML\Component , \Harbinger\GUI\Composite {
}
}
HTML\Leaf.php
namespace Harbinger\GUI\HTML;
/**
* @package Harbinger
* @subpackage GUI\HTML
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
interface Leaf extends \Harbinger\GUI\HTML\Component , \Harbinger\GUI\Leaf {
}
}
HTML\Traits\Component.php
namespace Harbinger\GUI\HTML\Traits;
/**
* @package Harbinger
* @subpackage GUI\HTML\Traits
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
trait Component {
HTML\Traits\Composite.php
namespace Harbinger\GUI\HTML\Traits;
/**
* @package Harbinger
* @subpackage GUI\HTML\Traits
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
trait Composite {
HTML\Traits\Leaf.php
namespace Harbinger\GUI\HTML\Traits;
/**
* @package Harbinger
* @subpackage GUI\HTML\Traits
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
trait Leaf {
HTML\View.php
namespace Harbinger\GUI\HTML;
/**
* @package Harbinger
* @subpackage GUI\HTML
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
class View extends \Harbinger\GUI\View implements Composite {
HTML\Head.php
namespace Harbinger\GUI\HTML;
/**
* @package Harbinger
* @subpackage GUI\HTML
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
class Head extends \Harbinger\GUI\AbstractComposite implements Composite {
HTML\Body.php
/**
* @package Harbinger
* @subpackage GUI\HTML
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
class Body extends \Harbinger\GUI\AbstractComposite implements Composite {
HTML\Div.php
/**
* @package Harbinger
* @subpackage GUI\HTML
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
class Div extends \Harbinger\GUI\AbstractComposite implements Composite {
HTML\Image.php
/**
* @package Harbinger
* @subpackage GUI\HTML
* @author Gabriel Heming <gabriel.heming@hotmail.com>
* @version 1.0.0
**/
class Image extends \Harbinger\GUI\AbstractLeaf implements Leaf {
E, não obstante, o seu uso:
$view->addClass('cl1')
Saída:
Como pode ver, apesar de uma certa complexidade (e essa é uma implementação que pode muito bem ser implementada), traits resolveram um problema que a herança simples não resolveria.
Há, também, alguns pontos a se destacar:
Agora eu vou dormir pois faz mais de três horas que estou escrevendo isso... se tiver algo errado eu corrigirei amanhã... :P