Septiembre 29, 2004

Un ejemplo del patr�n abstract factory

El patr�n Abstract Factory es uno de los patrones de construcci�n. B�sicamente, proporciona una interfaz que permite crear familias de objetos relacionados entre s�, sin especificar ( ni por lo tanto, conocer a priori ) sus clases concretas.

Voy a intentar explicarlo con un ejemplo un poco m�s �real�. Supongamos que quiero decorar mi casa. Puedo elegir entre dos l�neas de decoraci�n distintas ( en este caso, �Moderna�, o �Cl�sica� ). En principio s�lo voy a comprar las puertas y una televisi�n.

�Por qu� implementar una factor�a abstracta para resolver el problema?. Voy a intentar explicarlo sobre la marcha. En primer lugar voy a suponer que en mi ciudad hay dos tiendas de decoraci�n, la que vende elementos decorativos cl�sicos, y la que vende elementos decorativos modernos. Adem�s, supondr� que las dos tiendas venden los mismos elementos decorativos ( televisores y puertas ).

Bien, si yo quiero comprar una televisi�n, independientemente de lo moderna o cl�sica que sea, voy a la tienda y digo �quiero una televisi�n�. Adem�s, una televisi�n, sea moderna o cl�sica, me permite hacer las mismas cosas: encenderla, apagarla, subir y bajar el volumen, aunque no se haga de la misma forma ( en la moderna, lo hago con el mando a distancia, en la antig�a me tengo que levantar ).

Por lo tanto, ya s� que tengo que ir a la tienda, y pedir una televisi�n. Pero tambi�n puedo encargarle a un transportista que me traiga una televisi�n moderna. Yo no tengo que decirle nada m�s que eso, porque es el transportista es el que se encarga de pensar �vale, me ha dicho que quiere una tele moderna, por tanto, tengo que ir a la tienda donde venden las teles modernas, y pedir una tele�. Pues el transportista es la factor�a abstracta. Yo, como cliente, le pido una televisi�n moderna, y s� que voy a obtener una televisi�n de plasma de 42� que puedo manejar con el mando a distancia adjunto. �Qu� c�mo la ha conseguido el transportista?: no me importa. S�lo me importa que me la traiga.

Antes de empezar a implementarlo en actionScript, una consideraci�n te�rica importante. ActionScript no implementa clases abstractas. Es cierto que se pueden simular, pero no est�n implementadas, por lo que yo no voy a basar la arquitectura de este patr�n en una clase abstracta, sino en un interfaz.

Empecemos. Hemos dicho que tanto si voy yo en persona a la tienda, como si hablo con un transportista, lo �nico que tengo que hacer es decir "quiero una tele". Por lo tanto, tanto las tiendas como el transportista van a implementar un interfaz com�n ( si fu�ramos estrictos, deber�an heredar de una misma clase abstracta que implementara ese interfaz ).

El interfaz ser�:

import net.designnation.patterns.AbstractFactory.*

interface net.designnation.patterns.AbstractFactory.IFactoryActions
{
 public function getTV( ): ITVActions;
 	
 public function getDoor( ): IDoorActions;
}

Como puede verse, hay dos acciones posibles: getTV, y getDoor ( es decir, tr�eme una tele, y tr�eme una puerta ).

Por tanto, las dos tiendas ser�n algo as�:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.ModernShop implements IFactoryActions
{
 function ModernShop( )
 {
  trace( "He elegido la tienda moderna" );
 }
 
 public function getTV( ): ITVActions
 {
  return new BrandNewTV( );
 }
 	
 public function getDoor( ): IDoorActions
 {
  return new ModernDoor( );
 }	
}

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.ClassicShop implements IFactoryActions
{
 public function ClassicShop( )
 {
  trace( "He elegido la tienda cl�sica" );
 }
 
 public function getTV( ): ITVActions
 {
  return new OldTV( );
 }
 	
 public function getDoor( ): IDoorActions
 {
  return new ClassicDoor( );
 }
}

Como puede verse, las dos tiendas implementan el interfaz IFactoryActions ( implementan dos m�todos llamados getTV y getDoor, que devolver�n respectivamente, una televisi�n y una puerta ).

Atenci�n. Vamos a ver con m�s atenci�n el m�todo getTV( ).

public function getTV( ): ITVActions
{
 return new OldTV( );
}

�Instanciamos una clase OldTV pero el m�todo devuelve un interfaz?. Cierto. Eso es lo que nos permite tratar a todas las televisiones por igual, sean modernas, antig�as, o de gas. Porque lo que a nosotros nos interesa es poderle decir a la televisi�n: �enci�ndete�, y que �sta lo haga, como sea, pero que lo haga. Eso lo logramos haciendo que las dos clases que representan televisores implementen una interfaz com�n:

interface net.designnation.patterns.AbstractFactory.ITVActions
{
 public function pumpUpTheVolume( );
 public function shutUp( );
}

Y por tanto, la televisi�n moderna ser�:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.BrandNewTV implements ITVActions
{
 function BrandNewTV( )
 {
  trace( "He recibido mi televisi�n de plasma de 42 pulgadas" );
 }
 	
 public function pumpUpTheVolume( )
 {
  trace( "por supuesto, espera que busco el mando" );
 }
 	
 public function shutUp( )
 {
  trace( "Presionando esta tecla, el sonido se silencia" );
 }
}

Mientras que la cl�sica es:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.OldTV implements ITVActions
{
 function OldTV( )
 {
  trace( "Me he comprado una tv vieja" );
 }
 	
 public function pumpUpTheVolume( )
 {
  trace( "Como no tiene mando a distancia, me tengo que levantar a subir el volumen" );
 }
 	
 public function shutUp( )
 {
  trace( "Como no tiene mando a distancia...." );
 }
}

Lo mismo pasar�a con las puertas. Sea moderna o antigua, la puerta tiene dos posibles acciones, se puede abrir, y se puede cerrar, por lo tanto las dos clases que representan a puertas implementan un interfaz com�n:

interface net.designnation.patterns.AbstractFactory.IDoorActions
{
 public function open( );
 public function close( );
}

La puerta moderna:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.ModernDoor implements IDoorActions
{
 function ModernDoor( )
 {
  trace( "Constructor de la puerta moderna" );
 }
 	
 public function open( )
 {
  trace( "Abriendo la puerta moderna" );
 }
 	
 public function close( )
 {
  trace( "Cerrando la puerta moderna" );
 }
 	
}

Y la cl�sica:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.ClassicDoor implements IDoorActions
{
 function ClassicDoor( )
 {
  trace( "ClassicDoor constructor" );
 }
 	
 public function open( )
 {
  trace( "Puerta cl�sica abierta " );
 }	
 	
 public function close( )
 {
  trace( "Puerta cl�sica cerrada" );
 }
}

Recapitulemos. Tenemos dos tiendas. Ambas venden puertas y televisores. La tienda cl�sica vende puertas y televisores cl�sicos, y la tienda moderna vende puertas y televisores modernos.

Bien, pues nuestro transportista ser�a la abstract factory:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.AbstractFactory 
{
 public static var MODERN	: Number = 1;
 public static var CLASSIC	: Number = 2;
 	
 public static function getFactory( shopType: Number ): IFactoryActions
 {
  if ( ( shopType & MODERN ) == MODERN )
  {
   return new ModernShop( );
  }
  		
  if ( ( shopType & CLASSIC ) == CLASSIC )
  {
   return new ClassicShop( );
  }
 }
}

Esta clase implementa el mismo interfaz que las dos tiendas. Por tanto, yo le dir�, "oye, quiero una tele moderna", y me olvidar� de todos los detalles del proceso. Al final, tendr� algo a lo que podr� subir y bajar el volumen,�. �C�mo?:

import net.designnation.patterns.AbstractFactory.*

var factory: IFactoryActions = AbstractFactory.getFactory( AbstractFactory.MODERN );

var myTV: ITVActions = factory.getTV( );
myTV.pumpUpTheVolume( );

var myDoor: IDoorActions = factory.getDoor( );
myDoor.close( );

Escrito por Cesar Tardaguila en: Septiembre 29, 2004 07:08 AM | TrackBack
Comentarios

Hola Cesar,

Solo decirte que, como siempre, un post muy �til y un ejemplo con mucha "miga". Vuestro blog se est� conviertiendo en una gu�a de referencia de patrones de dise�o aplicados a ActionScript de visita obligada.

Gracias por compartirlo y un saludo ;)

Posted by: Carlos Rovira en: Septiembre 29, 2004 09:32 AM

Mis saludos, Cesar.

Quiero felicitarte por el excelente trabajo que aqui plasmaste.

Adios y te mando gracias desde Bolivia.

Posted by: Oscar Rueda en: Noviembre 17, 2004 09:46 AM