State Pattern em ColdFusion

On 13 de outubro de 2014, in CFML, Design Pattern, by andersonstraube

State é um padrão de projeto de software usado para permitir que um objeto altere o seu comportamento quando o seu estado muda. Ao utilizar este padrão, parecerá que o objeto mudou de classe.
O padrão State deve ser utilizado nas seguintes situações:
O comportamento de um objeto depende fortemente do seu estado e ele deve alterar o seu comportamento em tempo de execução dependendo do estado. Os métodos têm instruções condicionais grandes em que as condições dependem do estado do objecto. Este estado é normalmente representado por uma ou mais constantes do tipo enumerado. Frequentemente, vários métodos contém esta mesma estrutura condicional. O padrão State coloca cada ramo da instrução condicional numa classe separada. Desta forma, o estado do objecto pode ser tratado como um objecto ele próprio, o qual pode variar
Fonte: http://pt.wikipedia.org/wiki/State

Quando usar o Padrão State:


– Quando uma classe define muitos comportamentos;
– Quando classes relacionadas forem diferentes apenas no seu comportamento;
– Quando você precisar de diferentes variações de um mesmo algoritmo.
Para exemplificar o uso deste padrão vamos criar um código simples onde NÃO faz uso do State e depois veremos como aplicá-lo neste mesmo contexto.
O cenário em questão é um ventilador onde temos os estados/velocidade: Desligado, Baixa, Média e Alta. Cada vez que pressionamos o botão sua velocidade vai aumentando, quando chega no máximo (Alta) ele desliga, ou seja, Desligado -> Baixa -> Média -> Alta -> Desligado.

Ventilador.cfc
Exemplo de uma classe que NÃO usa o pattern State:


<cfcomponent name="Ventilador" displayname="Ventilador" output="false">
	<cfset variables.velocidade = 0 />

	<cffunction name="init" access="public" returntype="Ventilador">
		<cfset variables.velocidade = 0 />
		<cfreturn this />
	</cffunction>

	<cffunction name="pressionar" access="public" returntype="void">
		<cfscript>
		if (variables.velocidade == 0)
        {
            variables.velocidade = 1;
            WriteOutput("Velocidade baixa");
        }
        else if (variables.velocidade == 1)
        {
            variables.velocidade = 2;
            WriteOutput("Velocidade media");
        }
        else if (variables.velocidade == 2)
        {
            variables.velocidade = 3;
            WriteOutput("Velocidade alta");
        }
        else
        {
            variables.velocidade = 0;
            WriteOutput("Desligado");
        }
		</cfscript>
	</cffunction>
</cfcomponent>

O código acima funciona muito bem, porém como percebemos existe uma grande quantidade de lógica dentro de um mesmo método: pressionar(). Se aumentarmos a quantidade de níveis/velocidade a lógica será ainda maior, sem falar que precisamos definir o comportamento quando está em uma certa velocidade, ou seja, se é velocidade “Alta” devemos fazer tal operação. Outro detalhe é que não fica claro o que significa cada código da operação pois velocidade contém um valor que vai de 0 (zero) até 3, se adicionarmos uma velocidade “Media-Baixa” precisamos mover todos as velocidades superiores para um código imediatamente acima, dessa forma não fica um código muito claro e fácil de manter.

Como resolver esse problema? Aplicando o Pattern State!

Vamos aplicar o State e dividir essa lógica das velocidades em classes diferentes, afinal cada velocidade assume um comportamento peculiar.

Ventilador.cfc
Note que esta classe apenas delega a tarefa quando pressionamos o botão, não é função dela saber qual o próximo estado/velocidade. Isso é divisão de responsabilidade.


<cfcomponent name="Ventilador" displayname="Ventilador" output="false">
	<!--- Velocidade é o nosso "state" --->
	<cfset variables.velocidade = "" />

	<cffunction name="init" access="public" returntype="Ventilador">
		<!--- Seta o estado inicial do ventilador (desligado) --->
		<cfset this.setVelocidade( CreateObject("component","Desligado").init() ) />
		<cfreturn this />
	</cffunction>

	<cffunction name="pressionar" access="public" returntype="void">
		<cfset variables.velocidade.pressionar(this) />
	</cffunction>

	<cffunction name="setVelocidade" access="public" returntype="numeric">
		<cfargument name="velocidade" type="IState" required="true" />
		<cfset variables.velocidade = arguments.velocidade />
	</cffunction>
</cfcomponent>

IState.cfc
As velocidades devem implementar esta interface.


<cfinterface displayName="IState">
	<cffunction name="pressionar" returntype="void">
		<cfargument name="ventilador" type="Ventilador" required="true">
	</cffunction>
</cfinterface>

Desligado.cfc


<cfcomponent name="Desligado" implements="IState">
	<cffunction name="init" access="public" returntype="Desligado">
		<cfoutput>Desligado<br></cfoutput>
		<cfreturn this />
	</cffunction>

	<cffunction name="pressionar" access="public" returntype="void">
		<cfargument name="ventilador" type="Ventilador" required="true">
		<cfset arguments.ventilador.setVelocidade( CreateObject("component","Baixa").init() ) />
	</cffunction>
</cfcomponent>

Baixa.cfc


<cfcomponent name="Baixa" implements="IState">
	<cffunction name="init" access="public" returntype="Baixa">
		<cfoutput>Velocidade Baixa<br></cfoutput>
		<cfreturn this />
	</cffunction>

	<cffunction name="pressionar" access="public" returntype="void">
		<cfargument name="ventilador" type="Ventilador" required="true">
		<cfset arguments.ventilador.setVelocidade( CreateObject("component","Media").init() ) />
	</cffunction>
</cfcomponent>

Media.cfc


<cfcomponent name="Media" implements="IState">
	<cffunction name="init" access="public" returntype="Media">
		<cfoutput>Velocidade Media<br></cfoutput>
		<cfreturn this />
	</cffunction>

	<cffunction name="pressionar" access="public" returntype="void">
		<cfargument name="ventilador" type="Ventilador" required="true">
		<cfset arguments.ventilador.setVelocidade( CreateObject("component","Alta").init() ) />
	</cffunction>
</cfcomponent>

Alta.cfc


<cfcomponent name="Alta" implements="IState">
	<cffunction name="init" access="public" returntype="Alta">
		<cfoutput>Velocidade Alta<br></cfoutput>
		<cfreturn this />
	</cffunction>

	<cffunction name="pressionar" access="public" returntype="void">
		<cfargument name="ventilador" type="Ventilador" required="true">
		<cfset arguments.ventilador.setVelocidade( CreateObject("component","Desligado").init() ) />
	</cffunction>
</cfcomponent>

teste.cfm
Vamos testar o Ventilador:



<strong>Estado inicial:</strong><br>
<cfset ventilador = CreateObject("component", "Ventilador").init() />
<strong>Pressionar:</strong><br>
<cfset ventilador.pressionar() />
<strong>Pressionar:</strong><br>
<cfset ventilador.pressionar() />
<strong>Pressionar:</strong><br>
<cfset ventilador.pressionar() />
<strong>Pressionar:</strong><br>
<cfset ventilador.pressionar() />
<strong>Pressionar:</strong><br>
<cfset ventilador.pressionar() />
<strong>Pressionar:</strong><br>
<cfset ventilador.pressionar() />
<strong>Pressionar:</strong><br>
<cfset ventilador.pressionar() />
<strong>Pressionar:</strong><br>
<cfset ventilador.pressionar() />

A saída deverá ser:
Pressionar:
Velocidade baixa
Pressionar:
Velocidade media
Pressionar:
Velocidade alta
Pressionar:
Desligado
Pressionar:
Velocidade baixa
Pressionar:
Velocidade media
Pressionar:
Velocidade alta
Pressionar:
Desligado

Até a próxima, abraço.

Tagged with:  

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *