Dotstore
Canais iMasters

Linguagens + PHP + Desenvolvimento + Software

Padrões de Projeto - Value Object

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!


Comente também

5 Comentários

Adriano Alves
Adriano Alves

Muito bem explicado, Erico Almeida. Uma dúvida: sem utilizar o símbolo '$' antes de algumas variáveis e do operador 'this' funciona? Bem isso não sei, pode ser alguma conf. do ini, sei lá... Sei que seu intuito não é bem esse, mas o de ensinar o padrão VO, entretanto, tem muita gente iniciando em PHP e algo assim pode confundir e/ou dificultar o aprendizado. Obrigado, parabéns e aguardamos seu esclarecimento.

Erico Renato Oliveira de Almeida
Erico Renato Oliveira de Almeida

Houve um erro na edição do Artigo. O correto mesmo é colocar SIM o simbolo $ no 'this'. Portanto o correto ficaria '$this->variavel'
abraços

Marcio Muzzi
Marcio Muzzi

Érico, recomendo apenas você citar a fonte em seus artigos. Para quem interessar, este artigo é uma tradução do capítulo 2 do livro "PHP|Architect's Guide to PHP Design Patterns", que pode ser encontrado aqui:
www.amazon.com/PHP-Architects-Guide-Design-Patterns/dp/0973589825/

Crítico de Artigos
Crítico de Artigos

Érico, seu conteúdo é bom, mas recomendo antes de postar os artigos fazer algumas revisões do português que está bem ruim.

Fábio souto
Fábio souto

O artigo está bom. Só faltaram os cifrões, pois isso aqui é PHP né! Quem deseja levar estes conhecimentos de ValueObject e Imutabilidade adiante pode ler o artigo que escrevemos aqui: http://www.detonajogos.com/carloscoelho/blog/index.php?page=post&id=Y

Construimos uma simples hierarquia de classes, sendo que a classe mais básica é abstrata e se chama ValueObject. Depois seguimos construindo classes concretas do tipo String e EmailAddress, que re-aproveitam código já escrito.

Qual a sua opinião?

Comentários considerados ofensivos serão moderados.

Parceiros

IBM
PagSeguro
Internet Innovation
Dialhost
HostNet
Tecla
KingHost
DotStore
Dinamize