Quando passamos algum tempo modelando aplicações, é fato que vamos nos deparar com um mesmo problema várias e várias vezes. Ao longo do tempo também vamos encontrar maneiras de resolvê-los. A solução para esses problemas recorrentes é a chave para os padrões de projetos. Padrões de projetos não são métodos, frameworks ou componentes, são dicas de como resolver problemas conhecidos de forma eficaz.
Aqui no blog nós já falamos de alguns padrões de projetos como o Singleton, Transfer Object e o Typesafe Enum. Na dica Flex de hoje, vamos mostrar como implementar o padrão de projeto Factory Meythod que, assim como os citados anteriormente, tem papel arquitetural importante nas suas aplicações.
O padrão Factory provê uma abstração ou uma interface e permite que a subclasse ou a classe de implementação decida qual classe ou método deve ser instanciado ou chamado, baseado nas condições ou parâmetros informados.
Algumas de suas características são:
- Esconde as classes concretas do cliente.
- Junta duas ou mais hierarquias de classes separadas porém relacionadas.
- O objeto retornado pode ser tanto um objeto concreto quanto abstrato.
- As subclasses são quem especificam os objetos.
Para ilustrar esses conceitos utilizaremos um exemplo simples, vamos criar uma aplicação que informa a condição do tráfego nas estradas do RJ e SP.
Então mãos a obra.
Crie sua aplicação “FactoryMethodExample”:
Em seguida modifique sua Application conforme o código a seguir:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"> ? <s:controlBarContent> <s:Label text="CONDIÇÕES DO TRÁFEGO NAS ESTRADAS" fontWeight="bold" fontSize="16"/> <s:Button id="btTrafegoSP" label="Obtêm condições do tráfego em SP" /> s:controlBarContent> ? <s:TextArea id="report" left="50" right="50" bottom="50" top="50"/> ? s:Application> |
Basicamente, nossa aplicação deverá passar por três passos antes de informar as condições do tráfego ao usuário:
1. Obter as informações do tráfego.
2. Tratar o retorno dessas informações.
3. Retornar as informações formatadas.
Vamos criar um classe abstrata com um método Template Method que irá garantir que os três passos anteriores sejam respeitados.
Diferentemente do C# e do Java, o ActionScript 3.0 não suporta nativamente a criação de classes ou métodos abstratos. A classe abstrata é uma classe que sempre é estendida e nunca pode ser instanciada diretamente. Os métodos abstratos devem ser implementados pela subclasse e, como o ActionScript 3.0 não provê suporte para a palavra-chave “abstract”, vamos garantir esse comportamento lançando uma exceção.
Veja a classe abstrata “AbstractTrafego”:
package { public class AbstractTrafego { public final function inicializa():String { obtemInformacoes(); trataInformacoes(); return retornaInformacoes(); } ? public function obtemInformacoes():void { throw new Error("Método Abstrato"); } ? public function trataInformacoes():void { throw new Error("Método Abstrato"); } ? public function retornaInformacoes():String { throw new Error("Método Abstrato"); } ? } } |
Agora vamos criar a classe SPTrafego que estende nossa classe abstrata AbstractTrafego. Essa subclasse implementa os métodos chamados pelo Template Method Inicializa:
package { public class SPTrafego extends AbstractTrafego { private var condicoesTrafego:String = ""; private var arrCondicoesTrafego:Array = []; ? public override function obtemInformacoes():void { var trafego_1:Object = new Object(); trafego_1.rodovia = "Marginal Pinheiros"; trafego_1.condicao = "Congestionada em 6 km"; ? var trafego_2:Object = new Object(); trafego_2.rodovia = "Marginal Tietê"; trafego_2.condicao = "Congestionada em 8 km"; ? arrCondicoesTrafego.push(trafego_1); arrCondicoesTrafego.push(trafego_2); ? trace("Obtem as informações do tráfego em SP"); } ? public override function trataInformacoes():void { condicoesTrafego += "Última atualização: " + new Date() + "n"; condicoesTrafego += "Condições do Tráfego em SPn"; for each (var informe:Object in arrCondicoesTrafego) { condicoesTrafego += informe.rodovia + ": "; condicoesTrafego += informe.condicao + "n"; } condicoesTrafego += "r"; ? trace("Trata as informações do tráfego em SP"); } ? public override function retornaInformacoes():String { trace("Retorna as informações do tráfego em SP"); ? return condicoesTrafego; } } } |
Agora vamos testar nossa implementação. Insira o código a seguir no clique do botão btTrafegoSP:
<fx:Script> [CDATA[ ? protected function btTrafegoSP_clickHandler(event:MouseEvent):void { // Cria uma instância de SPTrafego var trafego:SPTrafego = new SPTrafego(); // Chama o Template Method definido na classe abstrata report.text += trafego.inicializa(); } ]]> fx:Script> |
Rode o código e veja o resultado.
Nesse momento nossa aplicação está implementando apenas o Template Method. Para entendermos as vantagens de se utilizar o Factory Method, digamos agora que vamos passar também a monitorar o tráfego das estradas do RJ e para isso vamos precisar nos conectar a uma fonte de dados diferente daquela usada para obter as informações de SP. Como teremos agora diferentes fontes de dados, vamos refatorar nosso código. O método ObtemInformacao não retorna nada atualmente, então vamos modificá-lo para que ele passe a retornar um objeto que represente as condições do tráfego.
Crie uma interface chamada ICondicao, que deverá ser implementada por todos esses objetos.
package { public interface ICondicao { function obtemDados():void; } } |
Agora vamos criar as classes que implementam a interface ICondicao:
package { public class SPCondicao implements ICondicao { public function obtemDados():Array { trace("Obtem dados do tráfego em SP"); ? var trafego_1:Object = new Object(); trafego_1.rodovia = "Marginal Pinheiros"; trafego_1.condicao = "Congestionada em 6 km"; ? var trafego_2:Object = new Object(); trafego_2.rodovia = "Marginal Tietê"; trafego_2.condicao = "Congestionada em 8 km"; ? var arrCondicoesTrafego:Array = []; ? arrCondicoesTrafego.push(trafego_1); arrCondicoesTrafego.push(trafego_2); ? return arrCondicoesTrafego; } } } |
Agora a classe que obtem os dados das estradas do RJ. Repare que as informações estão num formato diferente.
package { public class RJCondicao implements ICondicao { public function obtemDados():Array { trace("Obtem dados do tráfego no RJ"); ? arrCondicoesTrafego.push("Avenida Brasil: Congestionada em 5 km"); arrCondicoesTrafego.push("Ponte Rio-Niterói: Congestionada em 2 km"); ? return arrCondicoesTrafego; } } } |
Em seguida refatore a classe abstrata AbstractTrafego. Perceba que agora o método obtemInformacoes retorna um objeto do tipo ICondicao, que pode ser tanto um objeto relacionado às condições das estradas do RJ quanto de SP ou de qualquer outra cidade que venha a ser adicionada posteriormente:
package { public class AbstractTrafego { public final function inicializa():String { var condicao:ICondicao = obtemInformacoes(); trataInformacoes(condicao.obtemDados()); return retornaInformacoes(); } ? // Método Factory public function obtemInformacoes():ICondicao { throw new Error("Método Abstrato"); } ? public function trataInformacoes(arrCondicoesTrafego:Array):void { throw new Error("Método Abstrato"); } ? public function retornaInformacoes():String { throw new Error("Método Abstrato"); } ? } } |
Refatore agora a implementação da classe abstrata SPTrafego:
package { public class SPTrafego extends AbstractTrafego { private var condicoesTrafego:String = ""; ? public override function obtemInformacoes():ICondicao { return new SPCondicao(); } ? public override function trataInformacoes(arrCondicoesTrafego:Array):void { condicoesTrafego += "Última atualização: " + new Date() + "n"; condicoesTrafego += "Condições do Tráfego em SPn"; for each (var informe:Object in arrCondicoesTrafego) { condicoesTrafego += informe.rodovia + ": "; condicoesTrafego += informe.condicao + "n"; } condicoesTrafego += "r"; ? trace("Trata as informações do tráfego em SP"); } ? public override function retornaInformacoes():String { trace("Retorna as informações do tráfego em SP"); ? return condicoesTrafego; } } } |
E crie a classe RJTrafego:
package { public class RJTrafego extends AbstractTrafego { private var condicoesTrafego:String = ""; ? public override function obtemInformacoes():ICondicao { return new RJCondicao(); } ? public override function trataInformacoes(arrCondicoesTrafego:Array):void { condicoesTrafego += "Última atualização: " + new Date() + "n"; condicoesTrafego += "Condições do Tráfego no RJn"; for each (var informe:String in arrCondicoesTrafego) { condicoesTrafego += informe + "n"; } condicoesTrafego += "r"; ? trace("Trata as informações do tráfego no RJ"); } ? public override function retornaInformacoes():String { trace("Retorna as informações do tráfego no RJ"); ? return condicoesTrafego; } } } |
E por último mas não menos importante, vamos refatorar nossa Application:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"> ? <fx:Script> [CDATA[ ? protected function btTrafegoSP_clickHandler(event:MouseEvent):void { // Cria uma instância de SPTrafego var trafego:SPTrafego = new SPTrafego(); // Chama o Template Method definido na classe abstrata adicionaInforme( trafego ); } ? protected function btTrafegoRJ_clickHandler(event:MouseEvent):void { // Cria uma instância de SPTrafego var trafego:RJTrafego = new RJTrafego(); // Chama o Template Method definido na classe abstrata adicionaInforme( trafego ); } ? private function adicionaInforme(trafego:AbstractTrafego):void { report.text += trafego.inicializa(); ? } ? ]]> fx:Script> ? <s:controlBarContent> <s:Label text="CONDIÇÕES DO TRÁFEGO NAS ESTRADAS" fontWeight="bold" fontSize="16"/> <s:Button id="btTrafegoSP" label="Obtêm condições do tráfego em SP" click="btTrafegoSP_clickHandler(event)" /> ? <s:Button id="btTrafegoRJ" label="Obtêm condições do tráfego no RJ" click="btTrafegoRJ_clickHandler(event)" /> s:controlBarContent> ? <s:TextArea id="report" left="50" right="50" bottom="50" top="50"/> ? s:Application> |
Espero que tenham gostado. Baixe aqui o projeto de exemplo.
Até a próxima!









O Zend AMF é uma implementação feita em 
Tivemos o lançamento do Nokia N8, o primeiro dispositivo oficialmente a suportar Flash Lite 4.0. Além do 



