Gof – Builder

En esta ocasión vamos a hablar sobre el patrón de creación Builder, y su hace referencia a su propósito, la construcción de objetos complejos.

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

Simplificar la construcción de objetos complejos definiendo para ello una clase principal encargada de construir instancias de otras.

Otra definición valida es:

Separa la construcción de un objeto complejo de su representación para que el mismo proceso de construcción puede crear diferentes representaciones.

Debemos aplicar este patrón cuando:

  • El algoritmo para la construcción del objeto debe ser independiente de las partes que lo conforman.
  • Se requiere más de un constructor en una clase o el mismo comienza a tener muchas líneas de código.
  • Se requiere lógica en la construcción del objeto.
  • El proceso de construcción debe permitir diferentes representaciones del objeto que es construido.
  • La estructura interna de los objetos a construir es compleja.
  • Si los atributos internos son dependientes entre si.
  • Si utilizamos objetos que son difíciles, o poco convenientes, de obtener durante la creación.

Modelo del patrón Builder:

Modelo del patrón Builder

Modelo del patrón Builder

Lo primero que podemos notar es que tenemos un Director que será el encargado de la construcción de los objetos. También podemos observar que una de las clases más importantes aquí es Builder, la cual posee definiciones de los pasos que se deben seguir para poder construir un objeto – producto – en particular. Finalmente tenemos los constructores concretos del producto a retornar.

Para hacerlo más práctico, vayamos trabajando en un ejemplo. En este caso la idea es trabajar en un construcctor (o preparador) de cockteles 🙂 . Lo primero que vamos a representar es el constructor de cockteles, el cual vamos a llamar CocktailBuilder.

public abstract class CocktailBuilder
{
    protected Bebida bebida;

    public Bebida Bebida
    {
        get { return bebida; }
    }

    public abstract void BuildBebidaPrincipal();    
    public abstract void BuildBebidaSecundaria();
    public abstract void BuildHielos();
    public abstract void BuildAperitivo();
}

Podemos notar que posee métodos abstractos para la construcción de cada uno de los distintos componentes de la bebida. Estos serán implementados por los builders propios de cada bebida. Además podemos ver el Producto representado por el atributo del tipo Bebida, el cual va a estar definido de la siguiente manera:

public class Bebida
{
    private string _nombreBebida;
    private string _tipoBebida;
    private Dictionary<string, string> _contenido = new Dictionary<string, string>();

    public Bebida(string nombre, string tipo)
    {
        this._nombreBebida = nombre;
        this._tipoBebida = tipo;
    }

    public string this[string key]
    {
        get { return _contenido[key]; }
        set { _contenido[key] = value; }
    }

    public void Show()
    {
        Console.WriteLine("\n---------------------------");
        Console.WriteLine("{0}:", _nombreBebida.ToUpper());
        Console.WriteLine("Tipo de bebida: {0}", _tipoBebida);
        Console.WriteLine(" Bebida principal : {0}", _contenido["principal"]);
        Console.WriteLine(" Bebida secundaria : {0}", _contenido["secundario"]);
        Console.WriteLine(" Hielo: {0}", _contenido["hielo"]);
        Console.WriteLine(" Aperitivo : {0}", _contenido["aperitivo"]);
    }
}

Ahora vayamos a los builders concretos para cada tipo de bebida, en este vamos a crear uno para un gran clásico de Argentina el Fernet y otro para el trago Bloody Mary:

public class FernetBuilder : CocktailBuilder
{
    public FernetBuilder()
    {
        bebida = new Bebida("Fernet", "Digestivo");
    }

    public override void BuildBebidaPrincipal()
    {
        bebida["principal"] = "Fernet Branca";
    }

    public override void BuildBebidaSecundaria()
    {
        bebida["secundario"] = "Coca Cola";
    }

    public override void BuildHielos()
    {
        bebida["hielo"] = "2 cubitos";
    }
    public override void BuildAperitivo()
    {
        bebida["aperitivo"] = "Ninguno";
    }
}
public class BloodyMaryBuilder : CocktailBuilder
{
    public BloodyMaryBuilder()
    {
        bebida = new Bebida("Bloody Mary", "Refrescante");
    }

    public override void BuildBebidaPrincipal()
    {
        bebida["principal"] = "Vodka";
    }

    public override void BuildBebidaSecundaria()
    {
        bebida["secundario"] = "Zumo de Tomate";
    }

    public override void BuildHielos()
    {
        bebida["hielo"] = "1 cubito";
    }

    public override void BuildAperitivo()
    {
        bebida["aperitivo"] = "1 pizca de sal y pimienta negra";
    }
}

Bien, solo nos falta representar al Director, en nuestro caso lo vamos a denominar Barman:

public class Barman
{        
    public void Construct(CocktailBuilder cocktailBuilder)
    {
        cocktailBuilder.BuildBebidaPrincipal();
        cocktailBuilder.BuildBebidaSecundaria();
        cocktailBuilder.BuildHielos();
        cocktailBuilder.BuildAperitivo();
    }
}

Como podemos ver, este constructor tiene definido una serie “compleja” de pasos a seguir para conseguir nuestro refrescante trago!

Finalmente vamos a escribir el programa principal y a instanciar cada una de esta entidades:

class Program
{
    static void Main(string[] args)
    {
        CocktailBuilder builder;
        Barman barman = new Barman();

        builder = new FernetBuilder();
        barman.Construct(builder);
        builder.Bebida.Show();

        builder = new BloodyMaryBuilder();
        barman.Construct(builder);
        builder.Bebida.Show();

        Console.ReadLine();
    }
}

En la salida podemos observar que cada uno de los tragos fue preparado de formas diferentes:

Aplicando builder

Aplicando builder

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

  • Facilita el manejo y supervisación en la creación de objetos complejos,  y permite implementar caminos alternativos ante errores.
  • Facilita el control en la creación de objetos que recurren a recursos externos (por ejemplo a conexiones de base de datos).

– Desventajas:

  • Gran acoplamineto entre el Director, los Builders y el Producto, lo que implica un alto impacto ante cambios.
Anuncios

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