Comunidad de diseño web y desarrollo en internet online

Patrones de Diseño Creacionales

Después de la breve introducción a los patrones de diseño, llega el momento de explicar los patrones de diseño creacionales.

Los patrones de diseño creacionales se centran en resolver problemas acerca de cómo crear instancias de las clases de nuestra aplicación. A continuación nos centraremos en cada uno de ellos.

para abrir losl archivos .fla es necesario Adoble Flash Professional CS4 o superior


Abstract Factory


El patrón Abstract Factory o Fábrica Abstracta resuelve el problema de crear familias de objetos. Veamos un ejemplo típico de este patrón:

Imaginemos que estamos trabajando con Interfaces Gráficas de Usuario (GUI). Pensemos que, en nuestro programa, tenemos las clases Ventana y Boton. Pongamos, por ejemplo, que tenemos 2 interfaces diferentes: una con colores claros y otra con colores oscuros. Esto nos llevaría a tener 4 clases:

  • VentanaClara
  • VentanaOscura
  • BotonClaro
  • BotonOscuro

Cuando el usuario decida trabajar con colores claros, se deben crear instancias de VentanaClara y BotonClaro. Sin embargo, si el usuario decide utilizar la interfaz de colores oscuros, deberíamos crear instancias de VentanaOscura y BotonOscuro. La forma más básica de hacerlo sería de esta manera:

Código :

// A la hora de seleccionar la interfaz
var GUI:String = "clara"; // u "oscura";

// A la hora de crear un botón
if(GUI == "clara"){
new BotonClaro();
}else if(GUI == "oscura"){
new BotonOscuro();
}

// A la hora de crear una ventana
if(GUI == "clara"){
new VentanaClara();
}else if(GUI == "oscura"){
new VentanaOscura();
}

Esto implicaría realizar una comprobación de la interfaz seleccionada cada vez que se quiera crear una Ventana o un Boton.

La mejor opción en este caso sería utilizar el patrón Abstract Factory.

En este patrón se crean ciertas clases adicionales llamadas fábricas. Estas clases son las encargadas de crear los diferentes tipos de ventanas y botones. Veamos un ejemplo de su estructura:

Click en la imagen para ver en tamaño original


Identifiquemos cada clase del diagrama con las clases de nuestro ejemplo:

  • Cliente: Parte del programa que utilizará las fábricas y productos. Podría ser el archivo .fla principal, por ejemplo.
  • IProductoA: Interfaz que define un ejemplo de producto. Se correspondería con la clase Ventana de nuestro ejemplo.
  • ProductoA1 y ProductoA2: Los diferentes tipos de ese producto. Se corresponderían con la clases VentanaClara y VentanaOscura.
  • IProductoB: Interfaz que define otro ejemplo de producto. Se correspondería con la clase Boton de nuestro ejemplo.
  • ProductoB1 y ProductoB2: Los diferentes tipos de ese producto. Se corresponderían con la clases BotonClaro y BotonOscuro.
  • IFabrica: Interfaz que define las funciones de creación de productos. En nuestro ejemplo podría llamarse InterfazGrafica y definiría las funciones crearVentana():Ventana y crearBoton():Boton.
  • Fabrica1 y Fabrica2: Clases encargadas de crear los productos. En nuestro ejemplo, serían InterfazClara (que crearía instancias de VentanaClara y BotonClaro) e InterfazOscura (que crearía instancias de VentanaOscura y BotonOscuro).

Cabe señalar que podría haber más tipos de productos (Ventana, Boton, Icono, etc.) y más familias de estos (InterfazClara, InterfazOscura, InterfazAzul, InterfazRoja, etc.).

Una vez utilizado el patrón, el código anterior quedaría así:

Código :

// A la hora de seleccionar la interfaz
var GUI:InterfazGrafica = new InterfazClara(); // o new InterfazOscura();

// A la hora de crear un botón
GUI.crearBoton();

// A la hora de crear una ventana
GUI.crearVentana();

Según el tipo de InterfazGrafica instanciada, se crearán ventanas/botones de un tipo u otro dinámicamente, sin necesidad de comprobar a mano qué interfaz gráfica se está utilizando.

Descargar archivos de ejemplo Abstract Factory

Builder


El patrón Builder o Constructor se utiliza cuando queremos crear un producto que tiene diferentes partes. El siguiente ejemplo, basado en este otro, lo ilustra mejor:

Imaginemos la cocina de una pizzería donde se hacen pizzas. Las pizzas constan de varias partes (masa, salsa y relleno), y podríamos tener 2 cocineros, cada uno especialista en un tipo de pizza. Esto nos llevaría a tener 5 clases:

  • Cocina
  • Pizza
  • Cocinero
  • CocineroHawai
  • CocineroPicante

En una situación como esta el patrón Builder nos puede ayudar. Veamos un ejemplo de su estructura:



Click en la imagen para ver en tamaño original


Identifiquemos cada clase del diagrama con las clases de nuestro ejemplo:

  • Cliente: Parte del programa que utilizará el resto de clases. Podría ser el archivo .fla principal, por ejemplo.
  • Director: Clase que decide qué constructor se utiliza y cuando se debe construir el producto. Se correspondería con la clase Cocina de nuestro ejemplo.
  • IConstructor: Interfaz que define las funciones de creación de cada parte del producto y la función de obtención del producto resultante. En nuestro ejemplo se correspondería con Cocinero y definiría las funciones hacerMasa():void, utilizarSalsa():void y hacerRelleno():void.
  • Constructor1 y Constructor2: Clases encargadas de crear las partes del producto. En nuestro ejemplo, serían CocineroHawai y CocineroPicante.
  • Producto: Clase del producto en sí. Se correspondería con la clase Pizza de nuestro ejemplo.

De esta manera, un posible código de este ejemplo sería el siguiente:

Código :

var cocina:Cocina = new Cocina();

// Decidimos que se crearán pizzas hawaianas
cocina.elegirCocinero(new CocineroHawai());

// Creamos la pizza
var pizzaHawaiana:Pizza = cocina.nuevaPizza();

El código de la clase Cocina podría ser algo así:

Código :

package {
public class Cocina {

private var cocinero:Cocinero;

public function elegirCocinero(cocinero:Cocinero):void {
this.cocinero = cocinero;
}

public function nuevaPizza():Pizza {
cocinero.hacerMasa();
cocinero.utilizarSalsa();
cocinero.hacerRelleno();
}

}
}

Opcionalmente podría definirse un constructor para la clase Cocina que recibiese el cocinero como argumento, ahorrando la llamada a elegirCocinero().

Descargar archivos de ejemplo Builder

Factory Method


El patrón Factory Method o Método de Fábrica es una simplificación del patrón Abstract Factory. En un patrón Factory Method sólo existe un producto, no una familia de ellos. Veamos un ejemplo donde yo mismo he utilizado este patrón:

Imaginemos que deseamos crear un juego estilo Tetris. En este juego tendríamos diferentes tipos de piezas. Esto nos llevaría a tener una clase por cada tipo de pieza:

  • PiezaL
  • PiezaT
  • PiezaI
  • ...

Cada vez que se crea una pieza nueva, desearíamos seleccionar el tipo de pieza de forma aleatoria. La forma más básica de hacerlo sería la siguiente:

Código :

// Seleccionaríamos el tipo de pieza aleatoriamente
var tipo:uint = Math.random()*7; // 7 es el número de piezas diferentes en el Tetris

// Creamos dicha pieza
switch(tipo){
case 1:
new PiezaL();
break;
case 2:
new PiezaT();
break;
case 3:
new PiezaI();
break;
// ...
}

Sin embargo, sería mucho más sencillo poder hacerlo de una manera parecida a esta:

Código :

// Seleccionaríamos el tipo de pieza aleatoriamente
var tipo:uint = Math.random()*7; // 7 es el número de piezas diferentes en el Tetris

// Creamos dicha pieza
new Pieza[tipo]();


Para poder hacer algo así debemos utilizar el patrón Factory Method.

En este patrón se utilizan fábricas, al igual que en el patrón Abstract Factory. Estas fábricas son las encargadas de crear los diferentes tipos de piezas. Veamos un ejemplo de su estructura:



Click en la imagen para ver en tamaño original


Identifiquemos cada clase del diagrama con las clases de nuestro ejemplo:

  • Cliente: Parte del programa que utilizará las fábricas y productos. Podría ser el archivo .fla principal, por ejemplo.
  • IProducto: Interfaz que define el producto. Se correspondería con una clase Pieza en nuestro ejemplo.
  • ProductoA y ProductoB: Los diferentes tipos del producto. Se corresponderían con la clases PiezaL, PiezaT y PiezaI.
  • IFabrica: Interfaz que define las función de creación del producto. En nuestro ejemplo podría llamarse ICreador y definiría la función crearPieza():Pieza.
  • FabricaA y FabricaB: Clases encargadas de crear los productos. En nuestro ejemplo, serían CreadorL (que crearía instancias de PiezaI), CreadorT (que crearía instancias de PiezaT), CreadorI (que crearía instancias de PiezaI), etc.

Una vez utilizado el patrón, el código anterior quedaría así:

Código :

// Crearíamos una lista con todas las fábricas
var creadores:Vector.<ICreador> = new Vector.<ICreador>();
creadores.push(new CreadorL(), new CreadorT(), new CreadorI());

// Seleccionaríamos el tipo de pieza aleatoriamente
var tipo:uint = Math.random()*7; // 7 es el número de piezas diferentes en el Tetris

// Creamos dicha pieza
creadores[i].crearPieza();

De esta manera no necesitaríamos un switch, sino que se crearía la pieza a través del creador seleccionado.

Descargar archivos de ejemplo Factory Method

Prototype


El patrón Prototype o Prototipo resuelve el problema de duplicar objetos ya creados con anterioridad. Veamos un pequeño ejemplo de este patrón:

Imaginemos que tenemos un programa de dibujo por ordenador en el cual podemos crear círculos y cuadrados. Cuando se crea un círculo, éste tiene un radio de 50 píxeles y es de color rojo. Sin embargo, podemos redimensionar el círculo y cambiar su color. Cuando se crea un cuadrado, tiene 50 píxeles de lado y es de color azul. Hasta aquí todo perfecto.

Imaginemos ahora que el usuario decide crear un círculo y modifica su color y tamaño. Acto seguido, el usuario decide hacer una copia de dicho círculo. El código sería el siguiente:

Código :

var circuloNuevo:Circulo = new Circulo();
circuloNuevo.color = circuloExistente.getColor();
circuloNuevo.radio = circuloExistente.getRadio();

Uno de los problemas más inmediatos de hacerlo de esta manera es que, si se añaden nuevos atributos a la clase Circulo, habría que modificar el código en cada lugar donde se haya hecho una copia de un Circulo.

El patrón Prototype añade un método que permita crear una copia de un objeto. Veamos un ejemplo de su estructura:



Identifiquemos cada clase del diagrama con las clases de nuestro ejemplo:

  • Cliente: Parte del programa que utilizará las fábricas y productos. Podría ser el archivo .fla principal, por ejemplo.
  • IPrototipo: Interfaz que define el método clonar():IPrototipo. En nuestro ejemplo podría ser una clase llamada IObjetoGrafico.
  • Prototipo1 y Prototipo2: Las diferentes clases que implementarán el método de clonación. Se corresponderían con la clases Circulo y Cuadrado de nuestro ejemplo.

Una vez utilizado el patrón, el código anterior quedaría así:

Código :

var circuloNuevo:Circulo = circuloExistente.clonar();


Si ahora quisiéramos añadir nuevos atributos a la clase Circulo, sólo habría que modificar el método clonar():IPrototipo de la clase Circulo.

Por último, debe saberse que hay 2 tipos de clonación de objetos: profunda y superficial. Imaginemos un objeto A que contenga una referencia a un objeto B. Una clonación profunda haría que la copia del objeto A referenciase a una copia del objeto B. Por el contrario, una clonación superficial haría que la copia del objeto A apuntase al mismo objeto B que el objeto A original. Esto se entiende mejor con este ejemplo gráfico.

Descargar archivos de ejemplo Prototype

Singleton


El patrón Singleton se utiliza para no permitir que existan múltiples instancias de una clase, sino solamente una. Veamos un pequeño ejemplo de este patrón:

Imaginemos un programa que, al hacer click en un icono de ayuda, cree una ventana nueva con los documentos de ayuda del programa. Normalmente, si el usuario hiciese click en el botón nuevamente, se abriría una nueva ventana, y así sucesivamente.

Sin embargo, podríamos desear que, si la ventana de ayuda ya ha sido abierta, no se abra de nuevo. Para ello recurriríamos a un patrón Singleton, que aseguraría la existencia de una única ventana de ayuda en todo momento.

Veamos un ejemplo de la estructura del patrón Singleton:


Identifiquemos cada clase del diagrama con las clases de nuestro ejemplo:

  • Cliente: Parte del programa que utilizará las fábricas y productos. Podría ser el archivo .fla principal, por ejemplo.
  • Singleton: Clase que se quiere instanciar una sola vez. Se corresponde con la clase VentanaAyuda de nuestro ejemplo.


La forma de implementar el patrón Singleton en este ejemplo sería dotando a la clase VentanaAyuda de un método estático getInstancia():VentanaAyuda que comprobase si ya existe una instancia VentanaAyuda o si, por el contrario, se debe crear. De esta manera, el código para crear una nueva VentanaAyuda sería el siguiente:

Código :

var ayuda:VentanaAyuda = VentanaAyuda.getInstancia();

Sin embargo, debemos evitar que el usuario pueda crear instancias de VentanaAyuda mediante el constructor new VentanaAyuda(). La forma idónea de hacerlo es declarando el constructor como privado, de modo que no pueda ser llamado desde fuera de la clase VentanaAyuda. El problema es que ActionScript, de momento, no permite la declaración de constructores privados. Podemos solucionarlo de la siguiente manera:

Código :

package {
public class VentanaAyuda{

// Esta variable guardará la única instancia de ésta clase 
// que existirá en el programa
private static var instancia:VentanaAyuda;

// Ésta variable servirá para saber cuándo el constructor
// es invocado desde fuera de la clase
private static var llamadaInterna:Boolean;

// Constructor
public function VentanaAyuda(){

// Comprobamos si la llamada se ha producido
// desde fuera de esta clase
if (!llamadaInterna){

// Si se ha intentado instanciar la clase desde el
// exterior, la aplicación devolverá un error y no
// se creará ninguna instancia de VentanaAyuda.
throw new Error("Debes usar getInstancia()");

}

}

// Método que instanciará la clase una sola vez
public static function getInstancia():VentanaAyuda {

// Comprobamos si la instancia no se ha creado todavía
if (instancia == null) {

// Especificamos que la llamada
// al constructor es interna
llamadaInterna = true;

// Guardamos en la variable "instancia"
// una instancia de esta clase
instancia = new VentanaAyuda();

// Especificamos que las llamadas
// posteriores no serán internas
llamadaInterna = false;

}

// Por último, devolvemos la instancia existente
return instancia;

}

}
}


Observemos ahora el efecto que produciría este código de ejemplo:

Código :

new VentanaAyuda(); // Se devolvería un error y no se instanciaría la ventana
VentanaAyuda.getInstancia(); // Se devolvería una nueva instancia de VentanaAyuda
VentanaAyuda.getInstancia(); // Se devolvería la instancia ya existente que se creó en la linea anterior


Descargar archivos de ejemplo Singleton

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

El autor de este artículo ha cerrado los comentarios. Si tienes preguntas o comentarios, puedes hacerlos en el foro

Entra al foro y participa en la discusión

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate