PHP – Design Patterns: Factory Method

Elefante PHP

Nível Intermediário

O objetivo deste post é apresentar um exemplo de utilização do padrão Factory Method. Não detalha profundamente a respeito de padrões, mas abre uma introdução sobre Design Pattern, aproveitando a informação da Wikipedia.

Segundo a Wikipedia:
“Em engenharia de software, um padrão de desenho (português europeu) ou padrão de projeto (português brasileiro) (do inglês design pattern) é uma solução geral reutilizável para um problema que ocorre com frequência dentro de um determinado contexto no projeto de software. Um padrão de projeto não é um projeto finalizado que pode ser diretamente transformado em código fonte ou de máquina, ele é uma descrição ou modelo (template) de como resolver um problema que pode ser usado em muitas situações diferentes. Padrões são melhores práticas formalizadas que o programador pode usar para resolver problemas comuns quando projetar uma aplicação ou sistema” – Veja mais detalhes aqui.

Já tenho visto uma série de tutoriais explicando muito bem o padrão Factory Method, mas muitas vezes não tenho notado quase nenhum exemplo prático em PHP, ou seja, algo que você poderia usar em sua aplicação. Assim resolvi colocar um exemplo que poderá orientá-lo a entender melhor este padrão que não tem sido compreendido por muitos. Lembre-se que um padrão não é uma receita de bolo! 🙂
Note o diagrama abaixo, extraído da Wikipedia:

Factory Method
Factory Method

Perceba que o padrão Factory Method (Método Fábrica), é constituído por no mínimo quatro classes, em que você define uma interface forte. Neste padrão, você cria uma espécie de família de construtores de objetos relacionados ao mesmo contexto. Em resumo: Você cria normalmente uma classe abstrata e define como uma interface do criador. Esta define o padrão de métodos a serem invocadas nas classes filhas concretas do lado criador. Então cria uma classe abstrata que define a interface para a família de classes produtos que gerarão os obviamente os produtos. As classes concretas filhas do lado Produto são verdadeiramente moldes para entregas dos objetos pré-projetados. Vamos ver na prática como funcionam e para isso escolhi iniciar um projeto até interessante para criar interfaces gráficas. Uma fabrica de formulário HTML. Obviamente este script está bem simplificado, não vou implementar todos os métodos necessários para criar um formulário completo pois desviaria do foco, mas pode ser uma base para você pensar em algo maior! Assim, estes scripts são simplificados para cumprir o objetivo do post, e não contém também tratativas de erros, etc.
Neste Diagrama seguinte, temos o projeto. Então como você pode notar nós teremos uma fábrica que produz dois objetos, um referente a uma caixa de texto e outra com uma caixa de seleção. No alto (Pintado em roxo) estão as duas classes abstratas que definem as interfaces, uma para o criador e outra para o produto. Logo abaixo seguem as suas classes filhas concretas.
As classes do lado criador estão bem simplificadas e possuem apenas um método que implementa na verdade o único método da classe abstrata do lado criador.

Diagrama do Projeto conforme padrão Factory Method
Diagrama do Projeto conforme padrão Factory Method

Já do lado produto, a classe abstrata define pelo menos três métodos que são suficientes para atender a necessidade do nosso projeto. Perceba que são necessários pelo menos cinco atributos em cada classe filha do lado produto. Estes atributos serão utilizados pelos métodos para definir o nosso objeto que representará um elemento HTML.
Vamos começar por definir as duas classes abstratas que serão as interfaces. A primeira dei o nome de FactorySCreator:


abstract class FactorySCreator {
    
    public abstract function Create($name, $label, $size, $value);
  
}

A segunda classe abstrata é ObjectItemOfForm:


abstract class ObjectItemOfForm {
    
    public abstract function getValue();
    
    public abstract function setValue( $value );
    
    public abstract function getHTML();
    
}

OBS: Notem que até agora, não detalhei sobre criar ou não algum arquivo novo, pois como este post é de nível intermediário, acredito que você não tenha necessidade de ler aqui sobre estes detalhes.

Muito bem, agora precisamos das classes filhas concretas. Vamos começar pelas do lado produto que estendem ObjectItemOfForm:

Conheça nossa nova amiga que será molde para os objetos de caixa de textos, a classe ObjectTextBox


class ObjectTextBox extends ObjectItemOfForm {
    
    private $value;
    private $name;
    private $label;
    private $size;
    
    public function __construct($name, $label, $size, $value) {
        
        $this->name = $name;
        $this->label = $label;
        $this->size = $size;
        $this->value = $value;
    }
    
    
    public function getHTML() {
        
        $str = "<label for=\"txt_{$this->name}\">".strtoupper($this->label).":</label> <input type=\"text\" name=\"txt_{$this->name}\" size=\"{$this->size}\" value=\"{$this->value}\" />\n\r";
        
        return $tr;
    }

    public function getValue() {
        return $this->value;
    }

    public function setValue( $value ) {
        $this->size = $value;
    }

}

E então agora sua classe irmã, que produzirá a caixa de seleção, a classe ObjectComboBox:


class ObjectComboBox extends ObjectItemOfForm {
    
    private $value;
    private $name;
    private $label;
    private $size;
    private $items = array();
    
    public function __construct( $name, $label, $size, $value=null ) {
        
        $this->name = $name;
        $this->label = $label;
        $this->size = $size;
        $this->value = $value;
    }
    
    
    public function getHTML() {
        
        $str = "<label for=\"cmb_{$this->name}\">".strtoupper($this->label).":</label> <select name=\"cmd_{$this->name}\" size=\"{$this->size}\" >\n\r";
        
        if(count($this->items) > 0) {
            
            
            foreach( $this->items as $item => $key ){
                
                $str .= "\t\t<option value=\"{$item}\">{$key}</option>\n\r";
                
            }
            
        }
        
        $str .= "</select>";
        
        
        return $str;
     
    }

    public function getValue() {
        return $this->value;
    }

    public function setValue( $value ) {
        $this->size = $value;
    }
    
    public function addNewItem( $value, $label ){
        $this->items[$value] = $label;
    }


}

Você também deve ter notado que não entrei nos detalhes da implementação, mas este código é bem simples e quase alto explicativo. Mesmo assim, caso esteja muito complexo, fique tranquilo, em breve lançarei novos posts sobre PHP básico.

Precisamos agora apenas concluir apresentando as classes filhas concretas, do lado criador, que são as que irão interagir com as classes anteriores.

Te apresento, a nossa nova amiga FactoryTextBox


class FactoryTextBox extends FactorySCreator {
    
     public function Create( $name, $label, $size, $value ) {
        
         return new ObjectTextBox( $name, $label, $size, $value );  
         
    }

}

Note que o método Factory “Create”, na verdade traz um objeto gerado usando a classe ObjectTextBox e a parametrização é passada conforme recebido pelo método. Da mesma forma temos a classe FactoryComboBox :


class FactoryComboBox extends FactorySCreator {
    
     public function Create( $name, $label, $size, $value=null ) {
        
         return new ObjectComboBox( $name, $label, $size, $value );  
         
    }

}

A diferença nesta classe é que como um elemento HTML do tipo select não recebe um value nas tags que cercam as option, deixei este parâmetro como null e ele não é utilizado para esta classe. Mas ele precisa estar aí pois é padrão nas nossas interfaces.

Muito bem, terminado nossas classes, como é que testamos o funcionamento? Simples:


$objetoTexto = new FactoryTextBox();
$objetoCMD   = new FactoryComboBox();

$NovotxtBox1 = $objetoTexto->Create("nome", "nome", 40, "Alexandre Barbosa");
$NovotxtBox2 = $objetoTexto->Create("email", "email", 40, "alxbbarbosa@yahoo.com.br");

$NovoCMD1    = $objetoCMD->Create("Sexo", "Sexo", 1);
$NovoCMD1->addNewItem("M", "Masculino");
$NovoCMD1->addNewItem("F", "Feminino");

echo $NovotxtBox1->getHTML();
echo $NovotxtBox2->getHTML();
echo $NovoCMD1->getHTML();

Note que, precisamos instanciar um objeto FactoryTextBox() e outro FactoryComboBox(). Entretanto, depois podemos usar estes dois objetos “criadores” para gerar quantos outros objetos do tipo ObjectTextBox ou ObjectComboBox forem necessários em nosso projeto. É claro que existe um custo considerável em relação a este tipo especial de duplicação de código. Mas a manutenção é simplificada e é muito escalável. Podemos continuar criando todos os outros tipos de objetos para criar um formulário HTML completo. Tendo criado todas as classes, podemos até inicializar um objeto criador para cada item HTML. Podemos até melhorar este projeto usando o método Singleton. Mas isso é tema para um novo post. Espero que você faça bom uso deste exemplo.

[]’s

1 comentário

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.