GoF – Abstract Factory

En este primer post sobre los principios de diseños GoF vamos a hablar del patrón de creación Abstract Factory.

El propósito de este patrón es el siguiente:

Proporciona un contrato para crear familias de objetos relacionados sin tener que especificar un clase concreta.

Debemos aplicar este patrón cuando:

  • El cliente tiene que ser independiente del proceso de creación de los productos específicos.
  • Debemos convivir con más de una familia de productos dentro de la misma aplicación.
  • Necesitamos crear objetos como un conjunto y que sean compatibles entre ellos.
  • Deseamos proporcionar una colección de clases donde solo sea visible su contrato y no su implementación.

Modelo del patrón Abstract Factory:

Modelo Abstract Factory

Modelo Abstract Factory

En el modelo podemos identificar distintas familias de productos (en esta caso la 1 y la 2), y cada una de ellas compuestas por los productos A y B (esta más que claro que pueden existir n familias de objetos y n productos que las compongan). También podemos observar que existe una abstracción general para la fabricación de productos, la cual esta representada en el modelo como AbstractFactory, y sin importar de que familia de producto en concreto se trate, podremos a partir de la misma crear productos del tipo A o B (esto evita saber lo que realmente pasa por detrás).

Uno de los casos más representativos de este modelo en .NET es ADO.NET, en el cual nuestra Abstract Factory estaría representada por la clase DbProviderFactories y algunas de las familias de productos podrían ser SqlClient, OleDb o FireBirdClient entre otros. Entre los productos de estas familas podriamos encontrar objetos del tipo DbCommand, DbConnection DbException.

Pero mejor creemos nuestra propia abstract factory!… en nuestro ejemplo vamos a crear una factoría de encriptadores. Lo primero que vamos a definir son los productos que van a conformar la familia de objetos. Estos productos abstractos estarán representados por las clases Encriptador y Mensaje.

public abstract class Mensaje
{
    public Mensaje(string mensaje)
    {
        this.MensajeOriginal = mensaje;
    }

    public string MensajeOriginal { get; set; }
    public string MensajeEncriptado { get; set; }
    public Byte[] EncodedBytes { get; set; }
    public abstract void Mostrar();
}

public abstract class Encriptador
{
    public abstract void Encriptar(Mensaje m);
}

Básicamente la clase abstracta Encriptador representa un componente que puede encriptar texto y la clase abstracta Mensaje el contenido a encriptar. Ahora vamos a definir nuestra abstract factory de encriptadores la cual denominaremos CodificadorFactory. Dicha clase nos sirve como modelo para poder crear las distintas factories de encriptadores.

public abstract class CodificadorFactory
{
     public abstract Encriptador CrearEncriptador();

     public abstract Mensaje CrearMensaje(string mensaje);
}

Ahora a crear implementaciones “reales” de las factories de encriptadores!. En nuestro caso vamos a crear una factoría de encriptadores UTF8 y MD5.

public class UTF8Factory : CodificadorFactory
{
    public override Encriptador CrearEncriptador()
    {
       return new UTF8Encriptador();
    }

    public override Mensaje CrearMensaje(string mensaje)
    {
        return new UTF8Mensaje(mensaje);
    }
}

public class UTF8Encriptador : Encriptador
{
    private UTF8Encoding utf8 = new UTF8Encoding();

    public override void Encriptar(Mensaje m)
    {
        Byte[] encodedBytes = utf8.GetBytes(m.MensajeOriginal);

       foreach (Byte b in encodedBytes)
       {
            m.MensajeEncriptado += b;
        }

        m.EncodedBytes = encodedBytes;
    }
}

public class UTF8Mensaje : Mensaje
{
    public UTF8Mensaje(string mensaje)
       : base(mensaje)
    { }

   public override void Mostrar()
   {
        Console.WriteLine("Mensaje orginal:" + this.MensajeOriginal);
        Console.WriteLine("Mensaje encriptado con UTF8:" + this.MensajeEncriptado);
        Console.WriteLine("-----------------------------------------------------------");
    }
}

public class MD5Factory : CodificadorFactory
{
    public override Encriptador CrearEncriptador()
    {
        return new MD5Encriptador();
    }

    public override Mensaje CrearMensaje(string mensaje)
    {
       return new MD5Mensaje(mensaje);
    }
}

public class MD5Encriptador : Encriptador
{
    private MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

    public override void Encriptar(Mensaje m)
    {
       Byte[] encodedBytes = md5.ComputeHash(Encoding.Default.GetBytes(m.MensajeOriginal));
       for (int i = 0; i < encodedBytes.Length; i++)
       {
             m.MensajeEncriptado += encodedBytes[i].ToString("x2");
        }
        m.EncodedBytes = encodedBytes;
    }
}

public class MD5Mensaje : Mensaje
{
    public MD5Mensaje(string mensaje)
        : base(mensaje)
    { }

    public override void Mostrar()
    {
        Console.WriteLine("Mensaje orginal:" + this.MensajeOriginal);
        Console.WriteLine("Mensaje encriptado con MD5:" + this.MensajeEncriptado);
        Console.WriteLine("-----------------------------------------------------------");
    }
}

Desde nuestro “cliente” podemos invocar las distintas factories y utilizarlas de manera indistinta sin importar de que familia de encriptador en particular se trate:

class Program
{
    static void Main(string[] args)
    {
        CodificadorFactory utf8 = new UTF8Factory();
        var mensaje = utf8.CrearMensaje("Hola mundo!");
        var encriptador = utf8.CrearEncriptador();
        encriptador.Encriptar(mensaje);

        mensaje.Mostrar();

        CodificadorFactory md5 = new MD5Factory();
        mensaje = md5.CrearMensaje("Hola mundo!");
        encriptador = md5.CrearEncriptador();
        encriptador.Encriptar(mensaje);

        mensaje.Mostrar();

        Console.ReadLine();
    }
}

En la salida vemos que el resultado generado por los distintos encriptadores.

Aplicando abstract factory

Aplicando abstract factory

Adjunto el modelo de clases utilizado en el ejemplo para que puedan compararlo con el modelo de este patrón:

Modelo del ejemplo

Modelo del ejemplo

+ Ventajas

  • Aumenta la flexibilidad de la aplicación, mejores tiempos de compilación y ejecución.
  • Mejora el testing de la aplicación.

– Desventajas

  • Una mala definición del modelo puede traer aparejados problemas en la implementación del mismo, sobre todo a medida que crecen las familias de objetos.
Anuncios

4 comentarios en “GoF – Abstract Factory

  1. La pregunta es muy tonta, pero en tu ejemplo no me queda muy claro cual es la ventaja de usar un AbstractFactory, en ves de crear los objetos directamente:

    Mensaje md5Mensaje = new MD5Mensaje(“hola”);
    Encriptador md5Encriptador = new MD5Encriptador();
    md5Encriptador.Encriptar(md5Mensaje);
    md5Mensaje.Mostrar();

    AbstractFactory md5 = new MD5Factory();
    Mensaje md5Mensaje = md5.CrearMensaje(“Hola mundo!”);
    Encriptador md5Encriptador = md5.CrearEncriptador();
    md5Encriptador.Encriptar(md5Mensaje);
    md5Mensaje.Mostrar();

    Muchas gracias por la paciencia 🙂

    • Antes que nada gracias por visitar el blog!

      Además de las ventajas de aplicar Abstract Factory mencionadas en el post (tiempos de compilación y faciliad de testing) la idea de este patrón es por un lado ocultar la forma es que los objetos se construyen (primer respuesta a tu pregunta) y por el otro ofrecernos un mecanismo que nos permita ampliar una familia de objetos que poseen comportamiento y características “similares”.

      Siguiendo tu consulta y el ejemplo del post, si tuvieras que crear una nuevo encriptador sin utilizar AF, es muy posible que lo hagas con otra “estructura” diferente de los encriptadores que actualmente tenías. Entonces si quisieses reemplazar el uso de un encriptador por el nuevo, posiblemente tengas que modificar código ya que el comportamiento o atributos del mismo cambiaron. Si en lugar de eso agrego una factory a la familia para este nuevo encriptador, reemplazarlo en el código solo implica modificar la instancia del mismo.

      Espero que se entienda, y cualquier cosa la seguimos.
      Abrazos!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s