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( );