«

»

Apr 17

Print this Post

Strategy Pattern em ColdFusion

Strategy é um padrão de projeto de software (do inglês design pattern). O objetivo é representar uma operação a ser realizada sobre os elementos de uma estrutura de objetos. O padrão Strategy permite definir novas operações sem alterar as classes dos elementos sobre os quais opera. Definir uma família de algoritmos e encapsular cada algoritmo como uma classe, permitindo assim que elas possam ter trocados entre si. Este padrão permite que o algoritmo possa variar independentemente dos clientes que o utilizam.
Fonte: http://pt.wikipedia.org/wiki/Strategy

Motivação para usar o Padrão Strategy?

  • Quando um sistema possui vários componentes que têm semelhança estrutural porém com comportamentos diferentes;
  • Quando tem um algoritmo cujo cálculo pode variar dependendo dos parâmetros fornecidos;
  • Você não quer que o componente principal seja alterado e/ou “inflado” toda vez que um novo modo (estratégia) é desenvolvida/solicitada.


O diagrama abaixo representa o Padrão Strategy:

* Fonte da imagem: DoFactory.com

Para exemplificarmos o uso deste Pattern vamos criar uma aplicação que fará o cálculo do imposto de uma nota fiscal, porém hipoteticamente cada estado assume um tipo diferente de cálculo.

NotaFiscal.cfc
O mais comum seria fazer essa verificação dentro da nossa classe e calcular o valor do imposto:

<cfcomponent name="NotaFiscal" output="false">
	<!--- estou desprezando outros atributos por não ser o foco aqui --->
	<cfscript>
		variables.valor = 0;
	</cfscript>
	<cffunction name="calcularImposto">
		<cfargument name="estadoID" type="numeric" required="true">
		<!--- Imposto em SC  --->
		<cfif arguments.estadoID eq 1 >
			<cfreturn this.getValor() * 0.2 />
		<!--- Imposto no PR  --->
		<cfelseif arguments.estadoID eq 2 >
			<cfreturn (this.getValor() * 0.15) + 35 />
		</cfif>
	</cffunction>
	<cffunction name="setValor" access="public" returntype="numeric">
		<cfargument name="valor" type="numeric" required="true" />
		<cfset variables.valor = arguments.valor />
	</cffunction>
	<cffunction name="getValor" access="public" returntype="numeric">
		<cfreturn variables.valor />
	</cffunction>
</cfcomponent>

Dessa forma vai funcionar, mas e se por ventura surgir outro estado que seja só uma taxa fixa? Mais uma vez vamos inserir este estado em nossa classe:

<cffunction name="calcularImposto">
	<cfargument name="estadoID" type="numeric" required="true">
	<!--- Imposto em SC  --->
	<cfif arguments.estadoID eq 1 >
		<cfreturn this.getValor() * 0.2 />
	<!--- Imposto no PR  --->
	<cfelseif arguments.estadoID eq 2 >
		<cfreturn (this.getValor() * 0.15) + 35 />
	<!--- Imposto no RS --->
	<cfelseif arguments.estadoID eq 3 >
		<cfreturn 20 />
	</cfif>
</cffunction>

Dessa forma nosso componente perde a legibilidade, sem falar que adicionamos regras de negócios que na verdade não é ou pelo menos não deveria ser da responsabilidade da classe “NotaFiscal”. Imagine uma classe que é frequentemente utilizada pelo sistema, ao alterarmos qualquer bloco de código corremos o risco de criar um bug que afetará outras áreas do sistema, sem falar que começamos a “inflar” a classe com vários “IF’s” de acordo com cada estado.

A solução: como o Padrão Strategy resolve esse problema?

O Padrão Strategy é a solução perfeita para este exemplo, onde o cálculo do imposto se divide em um componente específico da sua própria responsabilidade, ou seja, para cada estado haverá um CFC, exemplo: ImpostoSC.cfc, ImpostoPR.cfc e ImpostoRS.cfc – todos eles herdarão uma única interface para realizar o cálculo do imposto.

IImposto.cfc
Criamos a nossa interface para que cada estado faça sua implementação:

<cfinterface>
	<cffunction name="calcular" returntype="numeric">
		<cfargument name="valor" type="numeric" required="true">
	</cffunction>
</cfinterface>

ImpostoSC.cfc
Vamos criar as classes responsáveis pelos cálculos do imposto nos 3 estados que implementa “IImposto.cfc”:

<cfcomponent name="ImpostoSC" implements="IImposto">
	<cffunction name="calcular" returntype="numeric">
		<cfargument name="valor" type="numeric" required="true">
		<cfreturn arguments.valor * 0.2 />
	</cffunction>
</cfcomponent>

ImpostoPR.cfc

<cfcomponent name="ImpostoPR" implements="IImposto">
	<cffunction name="calcular" returntype="numeric">
		<cfargument name="valor" type="numeric" required="true">
		<cfreturn (arguments.valor * 0.15) + 35 />
	</cffunction>
</cfcomponent>

ImpostoRS.cfc

<cfcomponent name="ImpostoRS" implements="IImposto">
	<cffunction name="calcular" returntype="numeric">
		<cfargument name="valor" type="numeric" required="true">
		<cfreturn 20 />
	</cffunction>
</cfcomponent>

NotaFiscal.cfc
Agora que já temos as classes responsáveis pelo cálculo, vamos alterar a NotaFiscal (método “calcularImposto”) para utilizar este pattern:

<cfcomponent name="NotaFiscal" output="false">
	<!--- estou desprezando outros atributos por não ser o foco aqui --->
	<cfscript>
		variables.valor = 0;
	</cfscript>
	<cffunction name="calcularImposto">
		<cfargument name="imposto" type="IImposto" required="true">
		<cfreturn arguments.imposto.calcular( this.getValor() ) />
	</cffunction>
	<cffunction name="setValor" access="public" returntype="numeric">
		<cfargument name="valor" type="numeric" required="true" />
		<cfset variables.valor = arguments.valor />
	</cffunction>
	<cffunction name="getValor" access="public" returntype="numeric">
		<cfreturn variables.valor />
	</cffunction>
</cfcomponent>

Por fim vamos criar o cfm responsável pelo teste:

<cfset notaFiscal = CreateObject("component", "NotaFiscal") />
<cfset notaFiscal.setValor(200) />
<!--- ImpostoSC => strategy --->
<cfset imposto = CreateObject("component", "ImpostoSC") />
<cfoutput>Imposto em SC: R$ #notaFiscal.calcularImposto(imposto)#<br></cfoutput>
<!--- ImpostoPR => strategy --->
<cfset imposto = CreateObject("component", "ImpostoPR") />
<cfoutput>Imposto no PR: R$ #notaFiscal.calcularImposto(imposto)#<br></cfoutput>
<!--- ImpostoRS => strategy --->
<cfset imposto = CreateObject("component", "ImpostoRS") />

A saída será:

Imposto em SC: R$ 40
Imposto no PR: R$ 65
Imposto no RS: R$ 20

Utilizando este padrão temos um código mais limpo, onde o cálculo do imposto é realizado separadamente e a classe NotaFiscal não se importa com cálculo do imposto assim como o tipo de classe que fará isso desde que ele implemente a interface “IImposto”. Com esse tipo de implementação a NotaFiscal não sofre nenhum impacto caso o número de classes derivadas aumente (um novo imposto de um estado por exemplo) e nem precisamos alterar o algoritmo de qualquer método “calcular” existente, basta criar uma nova classe, implementar a interface e abstrair o cálculo – desta forma o código que está usando esse componente pode decidir em tempo de execução qual cálculo utilizar sem afetar o componente principal.

Veja mais sobre outros Patterns:

Até a próxima, abraço.

Anderson Straube
Post original

 

Motivação para usar o Padrão Strategy?

– Quando um sistema possui vários componentes que têm semelhança estrutural porém com comportamentos diferentes;
– Quando tem um algoritmo cujo cálculo pode variar dependendo dos parâmetros fornecidos;
– Você não quer que o componente principal seja alterado e/ou “inflado” toda vez que um novo modo (estratégia) é desenvolvida/solicitada.

O diagrama abaixo representa o Padrão Strategy:

Permanent link to this article: https://ensina.me/coldfusion/strategy-pattern-em-coldfusion/

Leave a Reply