Observer Pattern em ColdFusion

On 11 de abril de 2012, in CFML, Design Pattern, by andersonstraube

O Observer é um padrão de projeto de software que define uma dependência um-para-muitos entre objetos de modo que quando um objeto muda o estado, todos seus dependentes sejam notificados e atualizados automaticamente. Permite que objetos interessados sejam avisados da mudança de estado ou outros eventos ocorrendo num outro objeto.
O padrão Observer é também chamado de Publisher-Subscriber, Event Generator e Dependents.
Fonte: http://pt.wikipedia.org/wiki/Observer

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

– Definir uma dependência um-para-muitos entre objetos para que quando um objeto mudar de estado, todos os seus dependentes sejam notificados e atualizados automaticamente;
– Quando um objeto deve ser capaz de avisar outros sem fazer suposições sobre quem são os objetos. Ou seja, sem criar um acoplamento forte entre os objetos;
– Conseguimos reduzir o uso do relacionamento bidirecional por meio de interfaces e classes abstratas, separando a abstração para ter um alto nível de coesão e baixo acoplamento.


O diagrama abaixo representa o Padrão Observer:

Diagrama UML Observer Pattern

Para exemplificarmos o uso deste Pattern vamos criar uma lista de usuários onde há um Log observando-o. Este log deverá exibir uma mensagem a cada atualização na lista, ou seja, a cada novo usuário inserido o log deverá registrar tal operação.

Para a implementação dentro do ColdFusion usaremos “interfaces” para resolver o problema, pois com ele conseguimos prover polimorfismo envolvendo classes não relacionadas por herança (de implementação).

ISubject.cfc
O objeto (sujeito) que irá notificar os observadores registrados precisa implementar esta interface (no nosso caso será a lista de usuários):


<cfinterface>

	<cffunction name="registrarObservador">
		<cfargument name="observador" type="any">
	</cffunction>

	<cffunction name="removerObservador">
		<cfargument name="observador" type="any">
	</cffunction>

	<cffunction name="notificarObservador">
		<cfargument name="args" type="any">
	</cffunction>

</cfinterface>

IObserver.cfc
O objeto que vai receber as notificações deve implementar essa interface (no nosso caso será o Log):


<cfinterface>

	<cffunction name="getInstanceID" returntype="String"/>

	<cffunction name="atualizar">
		<cfargument name="objeto" type="any">
		<cfargument name="args" type="any">
	</cffunction>

</cfinterface>

Usuarios.cfc
Essa classe implementa a interface de “ISubject”:


<cfcomponent implements="ISubject">

	<cfscript>
		variables._observadores = StructNew();
	</cfscript>


	<cffunction name="adicionarUsuario">
		<cfargument name="nome">

		<cfset this.notificarObservador( arguments.nome ) />
	</cffunction>


	<cffunction name="registrarObservador">
		<cfargument name="observador" type="any">

		<cfif not StructKeyExists(variables._observadores,arguments.observador.getInstanceID())>
			<cfset StructInsert(variables._observadores, arguments.observador.getInstanceID(), arguments.observador) />
		</cfif>

	</cffunction>


	<cffunction name="removerObservador">
		<cfargument name="observador" type="any">

		<cfif StructKeyExists(variables._observadores,arguments.observador.getInstanceID())>
			<cfset StructDelete(variables._observadores, arguments.observador.getInstanceID()) />
		</cfif>

	</cffunction>


	<cffunction name="notificarObservador">
		<cfargument name="args">

		<cfset var observador = "" />

		<cfloop collection="#variables._observadores#" item="key">
			<cfset observador = StructFind(variables._observadores, key) />
			<cfset observador.atualizar(this, arguments.args) />
		</cfloop>
	</cffunction>

</cfcomponent>

Log.cfc
Essa classe implementa a interface de “IObserver”:


<cfcomponent implements="IObserver">

	<cfset variables.instanceID = CreateUUID() >

	<cffunction name="getInstanceID" returntype="String">
		<cfreturn variables.instanceID>
	</cffunction>

	<!--- Aqui você faz a implementação que quiser quando o objeto sofrer alteração --->
	<cffunction name="atualizar">
		<cfargument name="objeto" type="any" hint="Objeto de origem">
		<cfargument name="args" type="any">

		<cfoutput><b>#arguments.args#</b> foi adicionado a lista de usuários<br></cfoutput>
	</cffunction>

</cfcomponent>

teste.cfm
Vamos criar o cfm de teste responsável por registrar o observador (no caso o “Log” em “Usuarios”), onde a cada usuário inserido na lista o Log será notificado.


<cfset variables.usuarios = CreateObject("component","Usuarios") />
<cfset variables.usuarios.registrarObservador( CreateObject("component", "Log") ) />

<cfset variables.usuarios.adicionarUsuario( "Anderson" ) />
<cfset variables.usuarios.adicionarUsuario( "Maria" ) />
<cfset variables.usuarios.adicionarUsuario( "João" ) />
<cfset variables.usuarios.adicionarUsuario( "Pedrinho" ) />

Ao executar o arquivo a saída será:


Anderson foi adicionado a lista de usuários
Maria foi adicionado a lista de usuários
João foi adicionado a lista de usuários
Pedrinho foi adicionado a lista de usuários

Vale ressaltar a parte do acoplamento de componentes, ou seja, a classe “Usuarios” não sabe e nem deve saber o que será feito no “Log” quando um usuário é adicionado, ela apenas avisa que houve uma mudança e os objetos ouvintes apenas faz o que tem que fazer. Aqui poderíamos criar um log que registra no banco de dados as alterações, poderíamos também criar uma classe para enviar um e-mail toda vez que um usuário é adicionado, etc.. A classe “Usuarios” não se preocupa sobre quem é, quantos são, bem como o tipo dos observadores – então a única responsabilidade desta classe é notificar os ouvintes/observadores registrados sobre a mudança. Com isso temos um código com alto nível de coesão e baixo acoplamento.

* Nota: não estou usando os atributos “access”, “returntype”, “hint” e outros nas funções para diminuir o código aqui, mas é importante que você sempre utilize-os em seus códigos.

Veja mais sobre outro Pattern:

Singleton em ColdFusion

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 *