«

»

Apr 23

Print this Post

Entendendo o Padrão Composite

Quando trabalhamos com Orientação a Objetos, existem momentos que enfrentamos alguns problemas conceituais e que se analisar-mos corretamente, misturamos a implementação e saímos da Orientação a Objetos.

Vamos pensar num cenário um pouco complexo.
Digamos que temos um sistema de gerenciamento de pedidos de automóveis em uma agência.
Porém em nosso processo de compras, temos dois tipos de produtos em modo geral.

  • Automóveis
  • Frotas

Onde Automóveis é uma compra de um ou mais automóveis por um comprador. E Frotas, seria onde uma empresa solicitaria a compra de vários automóveis afim de fazer uma frota de carros.

Pensando em modo grosseiro, pensaríamos em uma coleção de automóveis. Um objeto Automovel, populado em um array. Envia para o pedido e pronto.

Bom, a solução é até válida, mas uma coleção não tem identificação, não poderá ser tratada como um objeto, não tem integridade e etc… Como podemos fazer para manter essa lógica num padrão de Orientação a Objetos que não afete diretamente a estrutura dos objetos?

Existe um padrão chamado Composite.

Segue abaixo o diagrama de classes e o código fonte que demonstram a implementação do padrão como solução.


Resumindo, o Composite é um padrão estrutural que compartilha objetos em estruturas de árvore para representar hierarquias parte-todo, permitindo que tratemos objetos individuais e objetos compostos uniformemente. Ele é utilizado quando desejamos ignorar a diferença entre composição de objetos e objetos individuais.

Agora vamos fazer a implementação para entendermos na prática.

IProduto.cfc

Interface IProduto, contém todo o padrão e o mapeamento principal dos objetos.
<cfinterface>
	<cffunction name="setProduto" access="public" returntype="void">
		<cfargument name="value" type="string" required="true" />
	</cffunction>

	<cffunction name="setPrecoUnitario" access="public" returntype="void">
		<cfargument name="value" type="numeric" required="true" />
	</cffunction>

	<cffunction name="setQuantidade" access="public" returntype="void">
		<cfargument name="value" type="numeric" required="true" />
	</cffunction>

	<cffunction name="setComprador" access="public" returntype="void">
		<cfargument name="value" type="string" required="true" />
	</cffunction>
</cfinterface>

Automovel.cfc

Classe Automovel, Objeto para compras individuais.
<cfcomponent output="false" implements="IProduto">
	<cffunction name="init" access="public" returntype="Automovel">
		<cfscript>
			this.produto = "";
			this.precoUnitario = 0;
			this.quantidade = 0;
			this.comprador = "";
			return this;
		</cfscript>
	</cffunction>

	<cffunction name="setProduto" access="public" returntype="void">
		<cfargument name="value" type="string" required="true" />
		<cfset this.produto = arguments.value />
	</cffunction>

	<cffunction name="getProduto" access="public" returntype="string">
		<cfreturn this.produto />
	</cffunction>

	<cffunction name="setPrecoUnitario" access="public" returntype="void">
		<cfargument name="value" type="numeric" required="true" />
		<cfset this.precoUnitario = arguments.value />
	</cffunction>

	<cffunction name="getPrecoUnitario" access="public" returntype="numeric">
		<cfreturn this.precoUnitario />
	</cffunction>

	<cffunction name="setQuantidade" access="public" returntype="void">
		<cfargument name="value" type="numeric" required="true" />
		<cfset this.quantidade = arguments.value />
	</cffunction>

	<cffunction name="getQuantidade" access="public" returntype="numeric">
		<cfreturn this.quantidade />
	</cffunction>

	<cffunction name="setComprador" access="public" returntype="void">
		<cfargument name="value" type="string" required="true" />
		<cfset this.comprador = arguments.value />
	</cffunction>

	<cffunction name="getComprador" access="public" returntype="String">
		<cfreturn this.comprador />
	</cffunction>
</cfcomponent>

Frota.cfc

Classe Frota, Objeto composto contendo os atributos da frota e uma coleção de “automoveis” (Sem acento pois está representando o nome do objeto).
<cfcomponent output="false">
	<cffunction name="init" access="public" returntype="Frota">
		<cfscript>
			this.produto = "";
			this.precoUnitario = 0;
			this.quantidade = 0;
			this.comprador = "";
			this.viaturas = [];
			return this;
		</cfscript>
	</cffunction>

	<cffunction name="setProduto" access="public" returntype="void">
		<cfargument name="value" type="string" required="true" />
		<cfset this.produto = arguments.value />
	</cffunction>

	<cffunction name="getProduto" access="public" returntype="string">
		<cfreturn this.produto />
	</cffunction>

	<cffunction name="setPrecoUnitario" access="public" returntype="void">
		<cfargument name="value" type="numeric" required="true" />
		<cfset this.precoUnitario = arguments.value />
	</cffunction>

	<cffunction name="getPrecoUnitario" access="public" returntype="numeric">
		<cfreturn this.precoUnitario />
	</cffunction>

	<cffunction name="setQuantidade" access="public" returntype="void">
		<cfargument name="value" type="numeric" required="true" />
		<cfset this.quantidade = arguments.value />
	</cffunction>

	<cffunction name="getQuantidade" access="public" returntype="numeric">
		<cfreturn this.quantidade />
	</cffunction>

	<cffunction name="setComprador" access="public" returntype="void">
		<cfargument name="value" type="string" required="true" />
		<cfset this.comprador = arguments.value />
	</cffunction>

	<cffunction name="getComprador" access="public" returntype="String">
		<cfreturn this.comprador />
	</cffunction>

	<cffunction name="adicionaViatura" access="public" returntype="void">
		<cfargument name="value" type="Automovel" required="true" />
		<cfset ArrayAppend( this.viaturas , arguments.value ) />
	</cffunction>

	<cffunction name="getViaturas" access="public" returntype="Array">
		<cfreturn this.viaturas />
	</cffunction>
</cfcomponent>

Compras.cfc

Classe que manipula as instâncias dos objetos e gera os pedidos de compra.
<cfcomponent output="false">
	<cfscript>
		this.produto 		= "";
		this.totalProdutos 	= 0;
		this.valorTotal 	= 0;
		this.precoUnitario	= 0;
		this.comprador		= "";
		this.viaturas		= [];
		this.produtos		= [];
		public void function setProduto( String val ) {
				this.produto = arguments.val;
		}
		public String function getProduto() {
				return this.produto;
		}
		public void function setTotalProdutos( Numeric val ) {
				this.totalProdutos = arguments.val;
		}
		public Numeric function getTotalProdutos() {
				return this.totalProdutos;
		}
		public void function setPrecoUnitario( Numeric val ) {
				this.precoUnitario = arguments.val;
		}
		public Numeric function getPrecoUnitario() {
				return this.precoUnitario;
		}
		private void function setValorTotal( Numeric val ) {
				this.valorTotal = arguments.val * this.getTotalProdutos();
		}
		public Numeric function getValorTotal() {
				this.setValorTotal( this.getPrecoUnitario() );
				return this.valorTotal;
		}
		public void function setComprador( String val ) {
				this.comprador = arguments.val;
		}
		public String function getComprador() {
				return this.comprador;
		}
		public Void function setViaturas( Any val ) {
				if( isArray(arguments.val) ) {
					this.viaturas[ ArrayLen(this.viaturas)+1 ] = arguments.val;
				}
				else {
					this.viaturas[ ArrayLen(this.viaturas)+1 ] = "";
				}
		}
		public Void function setProdutos() {
				var = contador = ArrayLen(this.produtos)+1;
				this.produtos[ contador ] = StructNew();
				this.produtos[ contador ].totalProdutos = this.getTotalProdutos();
				this.produtos[ contador ].precoUnitario = this.getPrecoUnitario();
				this.produtos[ contador ].valorTotal = this.getValorTotal();
				this.produtos[ contador ].comprador = this.getComprador();
				this.produtos[ contador ].viaturas = this.viaturas;
				this.produtos[ contador ].produto = this.produto;
		}
		public Array function getProdutos() {
				return this.produtos;
		}
		private Void function popular( val ) {
				var args = arguments.val;
				this.setProduto( args.produto );
				this.setPrecoUnitario( args.precoUnitario );
				this.setTotalProdutos( args.quantidade );
				this.setComprador( args.comprador );
				if( StructKeyExists( args , "viaturas" ) && IsArray(args.viaturas) ) {
					this.setViaturas( args.viaturas );
				}
				else {
					this.setViaturas( "" );
				}
				this.setProdutos();
		}
		public Void function main(  ) {
			var palio = "";
			var uno = "";
			var chevette = "";
			var corsa = CreateObject("component","Automovel").init();
			var logan = CreateObject("component","Automovel").init();
			var frota = "";
			var retorno = "";
			/* Cria automóveis em compras individuais */
			palio = CreateObject("component","Automovel").init();
			palio.setProduto("Fiat Palio");
			palio.setPrecoUnitario(28000);
			palio.setQuantidade(1);
			palio.setComprador("Paulo José");
			this.popular( palio );
			uno = CreateObject("component","Automovel").init();
			uno.setProduto("Fiat Uno");
			uno.setPrecoUnitario(24000);
			uno.setQuantidade(1);
			uno.setComprador("Fernanda Lima");
			this.popular( uno );
			/* Cria frota - Objeto Composto */
			frota = CreateObject("component","Frota").init();
			frota.setProduto("Frota AutoCar");
			frota.setPrecoUnitario(685000);
			frota.setQuantidade(1);
			frota.setComprador("Empresa do Armando");
			chevette = CreateObject("component","Automovel").init();
			chevette.setProduto("Chevette LS");
			chevette.setPrecoUnitario(15000);
			chevette.setQuantidade(10);
			chevette.setComprador("Empresa do Armando");
			frota.adicionaViatura(chevette);
			corsa.setProduto("Corsa");
			corsa.setPrecoUnitario(35000);
			corsa.setQuantidade(5);
			corsa.setComprador("Empresa do Armando");
			frota.adicionaViatura(corsa);
			logan.setProduto("Logan");
			logan.setPrecoUnitario(45000);
			logan.setQuantidade(8);
			logan.setComprador("Empresa do Armando");
			frota.adicionaViatura(logan);
			this.popular( frota );
			/* Monta o resultado para ser escrito na tela */
			for( i=1; i<=ArrayLen(this.getProdutos()); i++ ){
				retorno &="<p>
					<b>Produto:</b> " & this.produtos[i].produto & "<br />
					<b>Preço:</b> " & LSCurrencyFormat(this.produtos[i].precoUnitario , 'local') & "<br />
					<b>Quantidade:</b> " & this.produtos[i].totalProdutos & "<br />
					<b>Comprador:</b> " & this.produtos[i].comprador & "<br />
					<b>Viaturas: </b> " & serializejson( this.produtos[i].viaturas[i], false ) & "<br />
				</p>";
			}
			writeOutput( retorno );
		}
	</cfscript>
</cfcomponent>

Para testar, basta implementarmos a classe Compras, da seguinte forma:

<cfscript>
	variables.compra = CreateObject("component","Compras");
	variables.compra.main();
</cfscript>

Como resultado você verá algo como:

Produto: Fiat Palio
Preço: R$ 28.000,00
Quantidade: 1
Comprador: Paulo José
Viaturas: “”

Produto: Fiat Uno
Preço: R$ 24.000,00
Quantidade: 1
Comprador: Fernanda Lima
Viaturas: “”

Produto: Frota AutoCar
Preço: R$ 685.000,00
Quantidade: 1
Comprador: Empresa do Armando
Viaturas: [{“PRODUTO”:”Chevette LS”,”QUANTIDADE”:10,”COMPRADOR”:”Empresa do Armando”,”PRECOUNITARIO”:15000},{“PRODUTO”:”Corsa”,”QUANTIDADE”:5,”COMPRADOR”:”Empresa do Armando”,”PRECOUNITARIO”:35000},{“PRODUTO”:”Logan”,”QUANTIDADE”:8,”COMPRADOR”:”Empresa do Armando”,”PRECOUNITARIO”:45000}]

Logicamente fizemos uma implementação bem simples para tratar um problema de desenvolvimento orientado a objetos. As necessidades desse tipo de utilização costumam ser mais complexas no dia-a-dia.

Espero que tenham gostado desse padrão, pois ele é bastante útil e bem legal de implementar.

Veja mais detalhes de Arquitetura de Softwares nos links abaixo:

Até a próxima.

Paulo Teixeira
Post original

Permanent link to this article: https://ensina.me/coldfusion/entendendo-o-padrao-composite/

Leave a Reply