Desenvolvimento

1 fev, 2016

Como fazer uma melhor reutilização de código PHP usando Traits – Parte 02

Publicidade

Na primeira parte deste artigo, aprendemos como usar traits PHP para organizar melhor as funcionalidades e para ser reutilizados por várias classes independentes.

Leia este artigo para aprender mais sobre as características dos recursos avançados dos traits, como a resolução de conflitos pelo uso de traits que definem as mesmas propriedades, alterar a visibilidade de um método traits na classe que o utiliza, reutilizar traits dentro de traits, exemplos de pacotes que fornecem características de uso geral de traits para utilização por muitos outros pacotes.

Introdução

Na primeira parte deste artigo, nós aprendemos que a partir do PHP 5.4 desenvolvedores podem usar traits para definir funcionalidades de código que podem ser reutilizados por muitas classes distintas, mesmo quando a sua principal funcionalidade é muito diferente.

Nesta parte, vamos cobrir tópicos mais avançados que lidam com aspectos que talvez você possa encontrar.

Resolução de conflitos

Nós terminamos a primeira parte com um exemplo simples de ambiguidade baseada no nome de propriedade.

Sabemos agora que a ambiguidade com propriedades cria problemas, mas o que acontece se dois traits vierem a usar a mesma implementação de métodos? A fim de evitar a falha do script com um erro fatal, a classe que utiliza traits deve resolver o conflito.

trait screwDrivers {
    
    public $driverType;
    
    public function returnTool() {
        
        return $this->driverType;
        
    }
    
    public function slottedDriver() {
        
        $this->driverType = 'slotted';
        
    }
    
    public function phillipsDriver() {
        
        $this->driverType = 'phillips';
        
    }
    
    public function starDriver() {
        
        $this->driverType = 'star';
        
    }
    
}

trait wrenches {
    
    public $wrenchType;
    
    public function returnTool() {
        
        return $this->wrenchType;
        
    }
    
    public function openWrench() {
        
        $this->wrenchType = 'open';
        
    }
    
    public function boxWrench() {
        
        $this->wrenchType = 'box';
        
    }
    
    public function allenWrench() {
        
        $this->wrenchType = 'allen';
        
    }
    
}

class myTools {
    
    use screwDrivers, wrenches {
        
        screwDrivers::returnTool insteadof wrenches;
        wrenches::returnTool as returnWrench;
        
    }
    
}

No exemplo acima, os dois traits implementaram um método returnTool, o que cria ambiguidade. Para resolver esse conflito, MyTools usa o operador insteadof para definir explicitamente qual método a classe usará.

Nesse caso, eu disse para usar o método returnTool da screwDriver em vez do que está na wrenches. Isso também significa que nós não temos uma maneira de acessar o método returnTool da wrenches, por isso nós temos que renomeá-lo com um apelido usando o operador “as”. Como você pode ver, eu fiz com que o método returnTool de whenches agora seja acessado como returnWrench.

Aqui está como ele seria usado no código:

$tools = new myTools();
$tools->slottedDriver();
$tools->allenWrench();
echo $tools->returnTool().'<br>';
echo $tools->returnWrench();

Perceba que o método returnTool está usando o método implementado no trait screwDrivers, e o método returnWrench está usando o método returnTool do trait wrenches.

Alterando a visibilidade do método

A propriedade “as” também pode ser usada para alterar a visibilidade de um método.

class myTools {
    
    use screwDrivers, wrenches {
        
        screwDrivers::returnTool insteadof wrenches;
        wrenches::returnTool as returnWrench;
        
        openWrench as protected;
        boxWrench as protected protectBoxWrench;
        
    }
    
}

No exemplo acima, o método openWrench está agora protegido, então ele só pode ser acessado de dentro da classe. O método boxWrench tem um apelido definido como protectBoxWrench, boxWrench ainda tem a sua visibilidade original, como pública, e protectBoxWrench só pode ser usado na classe, uma vez que está protegido.

Traits usando traits

Assim como uma classe, traits também podem usar traits, porém eles ainda precisam de uma classe para instanciá-los.

trait commonTools {
    
    use screwDrivers, wrenches {
        
        screwDrivers::returnTool insteadof wrenches;
        wrenches::returnTool as returnWrench;
        
        openWrench as protected;
        boxWrench as protected protectedBoxWrench;
        
    }
    
}
class myTools {
    
    use commonTools;
    
}

Para economizar espaço, o exemplo acima não inclui screwDrivers ou wrenches utilizados nos outros exemplos, então vamos supor que eles já foram definidos.

O trait commonTools define os outros traits que serão utilizados e gerencia a resolução de conflitos de modo que MyTools só tem que usar commonTools para acessar os métodos.

É importante notar que a resolução de conflito tem de ser tratada onde o trait está sendo usado, portanto, nesse caso, o trait commonTools está usando os traits screwDrivers e wrenches, o que significa que quaisquer conflitos devem ser resolvidos aqui, em commonTools.

De volta ao básico

Até agora, temos trabalhado sistematicamente em um caminho que envolve alguns tópicos avançados sobre o uso de traits. Embora seja importante para entender como resolver conflitos e usar operadores especiais, também é importante para não se perder no uso avançado de traits.

Como falado na introdução, traits são desenvolvidos principalmente para serem utilizados por várias classes em uma linguagem de única herança, como o PHP. O conjunto de ferramentas não precisa ser complicado, ele pode ser um conjunto de métodos que são comuns às classes e não relacionados a um projeto de desenvolvimento.

trait commonMethods {
    
    public function cleanUpString($string){
        
        $string = trim($string);
        
        return $string;
        
    }
    
    public function createStringArray($string){
        
        $stringArray = str_split($string);
        
        return $stringArray;
        
    }
    
}

class firstClass {
    
    use commonMethods;
    
}

class secondClass {
    
    use commonMethods;
    
}

O exemplo acima tem duas classes não relacionadas usando o mesmo trait de modo que cada classe tem agora acesso aos commonMethods. O código poder ser algo como isto aqui:

$first = new firstClass();
$second = new secondClass();
$cleanString = $first->cleanUpString('  I need to be cleaned up   ');
$stringArray = $second->createStringArray('Hello');

Aqui temos instanciada cada classe como seu próprio objeto, e cada classe pode acessar os métodos definidos no trait commonMethods.

A classe também pode definir sua própria implementação de um método definido no trait.

class secondClass {
    
    use commonMethods;
    
    public function createStringArray($string){
        
        $stringArray = str_split($string,2);
        
        return $stringArray;
        
    }
    
}

Com base na secondClass e implementando seu próprio método que divide a string em um array contendo dois caracteres por chave, veja o seguinte código.

$first = new firstClass();
$second = new secondClass();
$stringArray1 = $first->createStringArray('Hello');
$stringArray2 = $second->createStringArray('Hello');

$stringArray1 será um array no qual cada chave contém um caractere da string, e $stringArray2 será um array em que cada chave contém dois caracteres da string, uma vez que está usando o método implementado na classe que tem precedência sobre o método implementado no trait.

Traits também podem fornecer métodos abstratos aos quais a classe deve fornecer a implementação concreta.

trait commonMethods {
    
    abstract public function printString($string);
    
    public function cleanUpString($string) {
        
        $string = trim($string);
        
        return $string;
        
    }
    
    public function createStringArray($string) {
        
        $stringArray = str_split($string);
        
        return $stringArray;
        
    }
    
}

class firstClass {
    
    use commonMethods;
    
    public function printString($string) {
        
        echo $string;
        
    }
    
}

class secondClass {
    
    use commonMethods;
    
    public function printString( $string ) {
        
        $string = $this->cleanUpString($string);
        echo $string;
        
    }
    
    public function createStringArray( $string ) {
        
        $stringArray = str_split($string,2);
        
        return $stringArray;
        
    }
    
}

Como podemos ver aqui, o trait exige que as classes que o usam implementem o método printString. O FirstClass mostrará uma string como fornecida, enquanto o secondClass irá limpar a string e, em seguida exibi-la.

Traits também pode implementar métodos estáticos:

trait commonMethods {
    
    abstract public function printString($string);
    
    static public function cleanUpString($string){
        
        $string = trim($string);
        
        return $string;
        
    }
    
    public function createStringArray($string){
        
        $stringArray = str_split($string);
        
        return $stringArray;
        
    }
    
}

class firstClass {
    
    use commonMethods;
    
    public function printString($string){
        
        echo $string;
        
    }
    
}

Já que fizemos o método cleanUpString estático, ele pode ser acessado sem instanciar uma classe.

$cleanString = firstClass::cleanUpString(' Clean me up  ');

Exemplos de pacotes que fornecem traits de uso geral

No site PHP Classes, já existem vários bons exemplos ​de pacotes que fornecem traits que podem ser reutilizados por outros pacotes e reduzir o seu esforço de desenvolvimento. Aqui estão alguns exemplos de pacotes de trait:

Conclusão

Traits valem o seu tempo para aprender e implementar em seus próprios projetos. Linguagens de herança simples como o PHP tinham limitações na reutilização de código. Essas limitações não mais existem com a inclusão de traits.

Uma vez que você entenda plenamente o poder que os traits mcoloca em suas mãos, você pode começar a organizar melhor seus pacotes que reutilizam código que você escreveu anteriormente para diferentes fins.

***

Dave Smith faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://www.phpclasses.org/blog/post/308-How-to-Make-Better-Reuse-of-PHP-Code-using-Traits-Part-2-Advanced-Traits-Usage-Explained.html