Back-End

8 nov, 2007

Padrões de Projeto – Value Object

Publicidade

Salve, salve leitores do iMasters.

Depois de algum tempo afastado devido a alguns projetos, finalmente retorno para compartilhar um pouco mais de minha experiência com PHP. Vamos continuar na linha em que paramos.

Padrões de Projetos (Design Patterns);

Essa semana veremos os padrão Value Object ou, simplesmente, o padrão VO.

Em diversas aplicações orientadas a objetos, muitos objetos possuem uma Identidade. Uma importante classe de domínio como a classe Cliente ou a classe Fornecedor terá vários atributos um ID, um nome, um e-mail O valor desse atributos é que diferenciam diferentes objetos de sua classe. Portanto, um objeto com uma Identidade, PERSISTE, ou seja, ele existe durante toda aplicação.

Para nós programadores, um Cliente A é apenas um Cliente A e as mudanças nesse objeto durará durante toda execução da aplicação;

Mas existe situações em que alguns objetos NÃO precisam ter uma Identidade, pois esses objetos representam apenas características singulares de outros objetos;

Você pergunta: Não entendi!;

Vamos lá…Por exemplo, é comum usarmos objetos para representar Datas, Números, Dinheiro, etc.

Esses objetos não precisam de uma identidade, pois representam somente valores, ou seja, eles não precisam persistir durante toda a aplicação.

Eles não pertencem ao grupo de classes de Domínio e dificilmente aparecem em um Diagrama de Classes;

Digamos que são classes auxiliares…

Certo, e daí? Não basta eu criar uma classe Data ou uma classe Dinheiro?;

Sim, basta fazer isso, mas com alguns cuidados.

Veja o código abaixo e DESCUBRA o problema em se fazer SOMENTE isso.

Digamos que implementamos uma classe DinheiroRuim (Coloquei esse nome não porque dinheiro seja ruim :), mas porque está com uma implementação errada para o padrão VO ).

class DinheiroRuim{
	protected quantidade;
	
        public function __construct(quant=0){
	   this->quantidade = quant;
         }

       public function getQuantidade(){
	  return this->quantidade;
       }

         public function soma(valor){
	     this->quantidade = valor->getQuantidade();
         }

}

Agora implementamos a classe Trabalho que possui uma referência à classe DinheiroRuim (atributo salario);

class Trabalho{
	protected salario;
	
	public function  __construct(){
		this->salario = new DinheiroRuim(200);
         }

         public function getSalario(){
	    return this->salario;
         }
}

Criamos a classe Trabalhador que possui uma instância à classe Trabalho

class Trabalhador{
	public trabalho;
}

E Finalizamos com a classe de teste TesteDinheiroRuim;

class TesteDinheiroRuim{
	trabalho = new Trabalho();
	trabalhador1 = new Trabalhador();
	trabalhador2 = new Trabalhador();
	
	trabalhador1->trabalho = trabalho->getSalario();
	echo trabalhador1->trabalho->getQuantidade(); //IMPRIME 200
	
	trabalhador2->trabalho = trabalho->getSalario();
	echo trabalhador2->trabalho->getQuantidade(); //IMPRIME 200

	trabalhador1->trabalho->soma(trabalho->getSalario());
	echo trabalhador1->trabalho->getQuantidade(); //IMPRIME 400

          //ERRO  TRABALHADOR 2 TAMBÉM AUMENTOU O SALARIO
         echo trabalhador2->trabalho->getQuantidade(); //IMPRIME 400	

}

Ao rodarmos a aplicação acima, detectamos um erro!!!

Perceba que utilizamos um mesmo objeto Trabalho para os objetos Trabalhador1 e Trabalhador2;

No PHP 5, todos os objetos são passados por REFERÊNCIA, isso quer dizer que para OBJETOS, é passado o local de memória do objeto e NÃO uma CÓPIA;

Dessa forma, toda alteração que o objeto Dinheiro sofrer, PERSISTIRÁ por TODA a aplicação, ou seja, o objeto POSSUI uma IDENTIDADE;

Não preciso dizer que isso não é o desejado, já que queremos a classe Dinheiro somente armazenando valores para UM Trabalhador;

O PADRÃO VALUE OBJECT RESOLVE ESSE PROBLEMA!!

Nome do Padrão: VALUE OBJECT

Problema: Como construir uma classe leve e fácil de construir e descritiva, como Data ou Dinheiro ?

Solução: Objetos Leves devem possuir um comportamento semelhante ao tipos de dados primitivos do PHP (inteiro, float, string, etc): Se você atribui o mesmo objeto a duas diferentes variáveis e muda uma das variáveis, a outra variável deve permanecer imutável. Isso é o VO (Value Object).

Veja qual o comportamento que procuramos…

<?php
valor1 = 10;
valor2 = valor1;

echo valor1; // IMPRIME 10
echo valor2; //IMPRIME 10

valor2 = 25;

echo valor1; // IMPRIME 10
echo valor2; //IMPRIME 25

?>

Perceba que ao atribuímos a variável valor1 a valor2, ambos terão o mesmo valor (10);

Mas ao alterarmos qualquer das variáveis, a mudança não reflete em ambas as variáveis, ou seja, a variável NÃO POSSUI IDENTIDADE;

É esse o comportamento que esperamos obter com o Padrão VO;

E como faremos isso??

Vamos lá…

O Padrão VO possui 3 principios:

Seu valor é IMUTÁVEL, você não pode mudar o valor original;

É passado por cópia (não por referência), isto é, quando passado como parâmetro em um método, deve-se criar uma nova cópia do objeto;

Geralmente possui métodos de operação (Adição, Subtração, etc);

Para implementar o primeiro principio, basta retirarmos os métodos setters e adicionar os métodos de acessibilidade do atributo para bloqueio (private ou protected);

Alterando nossa classe DinheiroRuim, criamos a nova classe Dinheiro;

class Dinheiro{
	private quantidade;
	
           public function __construct(quant=0){
	     this->quantidade = (float)quant;
           }

           public function getQuantidade(){
	          return this->quantidade;
            }

          public function soma(valor){
	         return new Dinheiro(this->quantidade  valor->getQuantidade());
          }

}

Veja que alteramos o nível de acessibilidade do atributo quantidade para private. Dessa forma, restringimos o acesso direto à variável;

Também alteramos o método soma(); Note que em momento algum alteramos o valor original do atributo quantidade, estando de acordo com o primeiro principio do VO. Note também que, diferente da classe DinheiroRuim, o método retorno um NOVO objeto Dinheiro, simulamos assim uma passagem por valor e nunca teremos a mesma cópia do objeto distribuído em várias instâncias;

O Padrão VO é de fácil implementação, torna seu código elegante, de fácil manuntenção, evita aqueles erros inexplicáveis e pode ser utilizado em conjunto com outros padrões de projetos;

Bem pessoal, o VOé isso!

Próxima semana trago a vocês o famoso Padrão de Projeto Data Access Object (DAO)

Abraços e Sucesso!