«

»

May 07

Print this Post

Modelo Anêmico

Um dos fundamentos da Orientação a Objetos é a não exposição dos detalhes de implementação de um objeto. Já falei algumas vezes disso nos meus artigos.

Por isso usamos o encapsulamento da implementação, dessa forma não expomos nossos objetos e temos uma melhor manutenibilidade das nossas classes e já que esse encapsulamento garante a integridade de nossos objetos podemos garantir também que o usuário só consiga acessar nossos objetos através de um contrato definido com uma interface publica.

Mas muitas vezes começamos a implementar essas regras definindo nossos atributos como private e só permitindo o acesso a estes através de getters e setters:


<cfcomponent displayname="ShopCart">
	<cfscript>
		public ShopCart function init() {
			this.produtos = [];
			this.valorTotal = 0;
		}

		public Void function setProdutos( Produtos produto ) {
			ArrayAppend( this.produtos , arguments.produto );
		}

		public Array function getProdutos() {
			return this.produtos;
		}

		public Void function setValorTotal( Numeric valorTotal ) {
			this.valorTotal = arguments.valorTotal;
		}

		public Numeric function getValorTotal() {
			return this.valorTotal;
		}
	</cfscript>
</cfcomponent>

Primeiro vamos entender, que não estou falando de um Data Transfer Object (DTO) ou BEAN ou VO, BO…. Estou falando de uma classe simples. Poderia ser uma classe de negócios, um controller acessado pela interface para registrar dados do ShopCart de uma loja virtual.

Entendido isto, podemos analisar que a classe acima, possui alguns métodos que vão contra o conceito de encapsulamento da Orientação a Objetos.

Quando usamos getters e setters temos que tomar cuidado pois nem sempre o setter é a forma mais correta. Vamos ver o porque.

Nesse caso em particular, temos o atributo valorTotal que não deve ter seu valor “substituído” e sim somado ou subtraído de acordo com a operação realizada na interface do usuário.

Na realidade o valorTotal envolve muito mais do que pensamos, nossas classes devem ser inteligentes e devem possuir uma regra de negócio, lembre do conceito de Divisão de Responsabilidade a classe ShopCart tem a responsabilidade de determinar o valor total da compra, com seus impostos, fretes e tudo mais.

Se o objeto ShopCart depender de alguém para determinar esses valores, será burro e em caso de uma modificação na interface ou em outros objetos poderá acarretar interferências em seu correto funcionamento.

E então, quais são nossa opções

O princípio inicial é o conceito Tell, Don’t Ask, ou seja, você deve dizer aos objetos o que deve ser feito e não perguntar, como calcularValorTotal e evitar ficar perguntando sempre ao objeto como getValorTotal.

Podemos então implementar usando Injeção de Dependência para uma classe de lógica que fará esse calculo separadamente da classe ShopCart por exemplo uma classe “Compras”


<cfcomponent displayname="Compras">
	<cfscript>
		variables.ADD_PRODUTO = 1;
		variables.REMOVE_PRODUTO = 0;

		public Void function calcularValorTotal( ShopCart shopCart , Numeric valorProduto , Numeric imposto , Numeric tipoAcao ) {
			arguments.shopCart.setValorTotal(
				if( arguments.tipoAcao == variables.ADD_PRODUTO ) {
					arguments.shopCart.getValorTotal() + arguments.valorProduto + arguments.imposto );
				}
				else if( arguments.tipoAcao == variables.REMOVE_PRODUTO ) {
					arguments.shopCart.getValorTotal() - arguments.valorProduto - arguments.imposto );
				}
				else {
					throw("Objeto","Operação inválida");
					abort;
				}
			 );
		}
	</cfscript>
</cfcomponent>

Esse tipo de classe é muito bem estruturada, trafega mensagens bem claras entre os objetos, e em caso de manutenção ou mudança de regras é muito simples de ser modificado.

Porém isso que fizemos com a classe ShopCart é chamado de Modelo Anêmico (Anemic Domain Model), tiramos toda a responsabilidades de nossa classe, e nesse caso a transformamos simplesmente numa library de funções que faz o que alguém pede.

Pode funcionar, e até ser vantajoso, só devemos tomar cuidado para não transformarmos nossas classes nessa libraries… Uma opção a isso seria unir essa lógica com a classe ShopCart, inserindo responsabilidades a ela. Prestando a atenção que essa responsabilidade inserida tem haver com o escopo do objeto.


<cfcomponent displayname="ShopCart">
	<cfscript>
		variables.ADD_PRODUTO = 1;
		variables.REMOVE_PRODUTO = 0;

		public ShopCart function init() {
			this.produtos = [];
			this.valorTotal = 0;
		}

		public Void function setProdutos( Produtos produto ) {
			ArrayAppend( this.produtos , arguments.produto );
		}

		public Array function getProdutos() {
			return this.produtos;
		}

		public Numeric function getValorTotal() {
			return this.valorTotal;
		}

		public Void function calcularValorTotal( Numeric valorProduto , Numeric imposto , Numeric tipoAcao ) {
			if( arguments.tipoAcao == variables.ADD_PRODUTO ) {
				this.valorTotal = this.getValorTotal() + arguments.valorProduto + arguments.imposto );
			}
			else if( arguments.tipoAcao == variables.REMOVE_PRODUTO ) {
				this.valorTotal = this.getValorTotal() - arguments.valorProduto - arguments.imposto );
			}
			else {
				throw("Objeto","Operação inválida");
				abort;
			}
		}
	</cfscript>
</cfcomponent>

Como vimos, não implementamos mais o setter do atributo valorTotal, e mantivemos o getter. Mantivemos ele, pois ele faz parte do domínio. Para seguir essa regra, temos que repetir esse tipo de implementação para os produtos também, já que antes de inserir um produto, devemos ver se o mesmo ainda existe no estoque, se o cliente está fazendo um pedido para quando chegar o produto ele receber e etc… Assim teríamos uma classe sem setters e com recursos mais funcionais.

Podemos integrar padrões para gerar classes mais ricas e não simplesmente classes bobas sem inteligência nenhuma.

Percebam que a cada artigo que estudamos, vemos o envolvimento de padrões já estudados. Isso demonstra a importância de você ver sobre outros padrões. Não adianta você estudar esse padrão sem saber o que éInjeção de Dependências. Então, caso não tenha visto os outros artigos, essa é a hora de dar uma lida em cada padrão.

Espero ter sido claro nesse artigo, dessa vez, não vim passar um padrão novo, e sim alertar para preocupações que temos que ter quando estamos fazendo a arquitetura de um software.

Veja mais artigos de arquitetura de software abaixo:

Até a próxima, Abraços.

Paulo Teixeira
Post original

 

 

Permanent link to this article: http://ensina.me/coldfusion/modelo-anemico/

Leave a Reply