Cascade-Lambda pattern

Es momento de hacer más interesante el patrón anterior. Antes que nada recordemos cual fue el resultado final en ese momento:

new EmailManager()
    .Client("client")
    .From("from@sebys.com.ar")
    .To("to@sebys.com.ar")
    .Subject("Envio email")
    .Body("Este es el contenido del e-mail")
    .Send();

Como podrán notar, el método Send() es el que realiza la “acción final” en todas las instancias de EmailManager. Esto nos permite deducir dos cosas, la primera es que toda instancia de EmailManager “finaliza” cuando realizamos el envío de correo y la segunda es que implícitamente estamos diciendo que para realizar un nuevo envío de correo necesitamos crear una nueva instancia. Aunque el comportamiento es bastante lógico, nuestra API no es explicitamente clara en su comportamiento.

Para hacer explicito este comportamiento, podemos hacer uso de las expresiones lambda.

Para trabajar con expresiones lambda vamos a convertir el método Send() es un método estático y cambiar su firma para que acepte un delegado Action<T>. Este delegado debe tomar como parámetro una instancia de EmailManager. Por último vamos a invocar la acción dentro del método, previo a realizar el envío propiamente dicho:

public static void Send(Action action)
{
    action(new EmailManager());
    Console.WriteLine("Enviar correo...");
}

Ahora veamos como cambia la forma de invocar a EmailManager:

static void Main(string[] args)
{
    EmailManager.Send((mail) => mail.Client("client")
                                   .From("from@sebys.com.ar")
                                   .To("to@sebys.com.ar")
                                   .Subject("Envio email")
                                   .Body("Este es el contenido"));
}

Con esto logramos eliminar toda confusión acerca de como debe realizarse el envió del e-mail, ya que lo que el método Send() establece claramente donde debe “construirse” el correo y que puede enviarse una única vez por instancia.

¡Espero que les sea de utilidad!

Cascade Pattern

A cuantos de nosotros nos tocó alguna vez escribir código como el que vamos a ver a continuación?

public class EmailManager
{
    public string Client { get; set; }
    public string From { get; set; }
    public string To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }

    public void Send()
    {
        // Enviar correo...
    }
}

Teniendo la clase EmailManager, la instanciación seria mas o menos de la siguiente forma:

var email = new EmailManager();
email.Client = "client";
email.From = "from@sebys.com.ar";
email.To = "to@sebys.com.ar";
email.Subject = "Envio email";
email.Body = "Este es el contenido del e-mail";

email.Send();

Si prestan atención al código notarán que hay algunas cositas que podríamos mejorar. En primer lugar, vemos que la variable “email” se repite un importante número de veces antes de realizar la acción final, esto nos lleva a pensar que tenemos código redundante que se puede evitar. Por otro lado no queda claro si para realizar un nuevo envío puedo utilizar la instancia ya creada o debería definir una nueva, lo que puede resultar confuso para cualquier desarrollador.

Para resolver esto hagamos algunas pequeñas adaptaciones al código:

public class EmailManager
{
    private string client { get; set; }
    private string from { get; set; }
    private string to { get; set; }
    private string subject { get; set; }
    private string body { get; set; }

    public EmailManager Client(string client)
    {
        this.client = client;
        return this;
    }

    public EmailManager From(string from)
    {
        this.from = from;
        return this;
    }

    public EmailManager To(string to)
    {
        this.to = to;
        return this;
    }

    public EmailManager Subject(string subject)
    {
        this.subject = subject;
        return this;
    }

    public EmailManager Body(string body)
    {
        this.body = body;
        return this;
    }

    public void Send()
    {
        // Enviar correo...
    }
}

Lo que hicimos es agregar a cada propiedad un método que devuelve la instancia del objeto creado, lo que nos permite ir invocando nuevos métodos sin necesidad de escribir una nueva línea.

Veamos como es la nueva forma de instanciarlo:

new EmailManager()
    .Client("client")
    .From("from@sebys.com.ar")
    .To("to@sebys.com.ar")
    .Subject("Envio email")
    .Body("Este es el contenido del e-mail")
    .Send();

Como resultado, se logra eliminar código duplicado logrando más legibilidad y simplicidad de uso.

Algunos frameworks que nos ayudan con esta tarea son FluentValidation y NBuilder. Por último, un patrón alternativo y del que ya habíamos hablado en el post es Fluent Interface.

Espero que les sea de utilidad!

Patrón PRG

En esta oportunidad vamos a hablar del patrón PRG (Post-Redirect-Get) el cual es aplicable en el desarrollo web. Tal como el nombre nos sugiere, lo que se logra con esta técnica es que la respuesta a una petición POST sea una re-dirección que nos permita obtener una nueva página por medio de una petición GET.

Antes que nada veamos, utilizando un escenario bastante común en los sitios web, el problema de no aplicar este patrón. Imaginemos un formulario de contacto, en donde el usuario carga información y se envía a un servidor web que la recibe y procesa. Por último informamos al usuario que la operación se realizó con éxito por medio de una nueva página.

En este último punto es donde arriba el problema, ya que si el usuario por esas cosas de la vida se le ocurre actualizar esta última página (F5) aparecerá el siguiente cuadro de dialogo:

Dialogo

Dialogo

El primer problema con esta advertencia es que el usuario posiblemente no entienda el mensaje (en el cual se pregunta si quiere volver a enviar la información del formulario). Por este motivo posiblemente intente reenviar la información cuando ya lo hizo!.

En el caso de hacerlo (botón “Reintentar”) se volverá a realizar la petición POST y nuevamente estaremos procesando la lógica del formulario de contacto (o el formulario que corresponda). Esto, entre otros problemas, nos puede generar registros duplicados, notificaciones duplicadas, etc …  :/

Para prevenir estos casos lo que PRG nos sugiere es lo siguiente:

  1. Recibimos una petición POST con los datos del formulario y ejecutamos la lógica que corresponda.
  2. Respondemos al cliente con una redirección (código de estados HTTP 30x) para que el navegador sepa que debe solicitar otra página (en el ejemplo sería la pantalla que informa que los datos de contacto se enviaron correctamente).
  3. El navegador obtiene esta página mediante una petición GET.

Vista la teoría, vamos a la práctica.

Aclaración: en el siguiente ejemplo voy aplicar el patrón PRG en una aplicación ASP.NET MVC pero es totalmente posible hacerlo en cualquier otra tecnología web (WebForms, PHP…).

Siguiendo el ejemplo de arriba, nuestro método de acción debería quedar  mas o menos así:

Aplicando el patrón PRG

Aplicando el patrón PRG

Muy simple, no?! En un post anterior vimos lo facil que es hacer re-direcciones en ASP.NET MVC. Veamos la respuesta del servidor luego de procesar la petición POST:

Respuesta del servidor

Respuesta del servidor

En primer lugar la respuesta a la petición POST es una re-dirección (HTTP Status 302). En segundo lugar se solicita la página “de agradecimiento” por medio de una petición GET, la cual es devuelta con éxito (HTTP Status 200). Si ahora al usuario se le ocurriera actualizar la página, el navegador le retornará la última petición realizada (petición GET).

Como pueden ver, aplicar esta patrón es muy simple, aún más con ASP.NET MVC (solo basta con modificar la respuesta en nuestro método de acción).

Espero que les sea de utilidad 🙂

DIP – Principio de Inversión de Dependencias

Un post que tenía pendiente hace mucho tiempo era acerca del quinto principio SOLID llamado Principio de Inversión de Dependencias (Dependency Inversion Principle). Este principio nos dice que “dependamos de abstracciones, no de concreciones”. Tío Bob plantea dos puntos en la definición de este principio:

A. Las clases de alto nivel no deberían depender de las clases de bajo nivel. Ambas deberían depender de las abstracciones. B. Las abstracciones no deberían depender de los detalles. Los detalles deberían depender de las abstracciones.

Este principio, en conjunto con el resto de los principio SOLID, están orientados a reducir problemas relacionados con el mal diseño (aún cuando tengamos el código bien organizado en clases). Tio Bob  dice que en todo mal diseñose presentan alguna de las siguientes características:

  • Rigidez: un cambio afecta a muchas partes de un sistema.
  • Fragilidad: cada cambio genere problemas en lugares inesperados.
  • Inmovilidad: imposible de reusar.

Una forma muy simple de detectar un mal diseño es cuando tenemos “miedo” de tocar el código, porque creemos que en alguna parte algo se va a romper. Veamos un ejemplo de código mal diseñado y como podemos mejorarlo aplicando los principios SOLID, sobre todo DIP. Continuando con el mismo tema de los últimos post de la serie, supongamos que tenemos la clase PrecesoAutorizacionTarea que contiene el método Iniciar(). Este método se encarga, entre otras cosas, de loggearinformación acerca del inicio del proceso y el plazo máximo de aprobación de la tarea:

Definición clase ProcesoAutorizacionTarea

Definición clase ProcesoAutorizacionTarea

Es fácil detectar varios “errores” de diseño en la implementación del método. El primero de ellos es que es un diseño rígido. Supongamos que tenemos que agregar una nueva propiedad a la clase TxtLog para especificar el path o file del archivo de log a utilizar? Esta claro que deberíamos cambiar todas las instancias de TxtLog en el código, salvo que quisiéramos registrar “todo” en el archivo de log especificado por default (pero en tal caso, no tendría sentido dicha propiedad).

Para resolverlo, supongamos que decido especificar esta propiedad “a mano” (logger.Path = “…”;) en todas las porciones de código que sean necesarias (es decir, donde no me sea útil el archivo por default). En tal caso nos encontrarímos con un diseño frágil, ya que si nos olvidamos de especificarlo, estaríamos registrando información en un archivo de log, que sería el incorrecto. Algo similar ocurre con el calculo del plazo máximo de autorización de la Tarea… qué pasa si necesito realizar el mismo cálculo en otro módulo? O qué pasa si ahora tengo un nuevo tipo de tarea? Es realmente responsabilidad de la clase ProcesoAutorizacionTarea realizar dicho cálculo?… esta claro que las respuestas a estas preguntan nos conducen ante un mal diseño. Bien, para poder solucionar estos problemas, vamos a realizar una serie de cambios, el primero respecto al lugar donde se instancia el objeto TxtLog:

Resolviendo problemas de rigidez y fragilidad

Resolviendo problemas de rigidez y fragilidad

Como podemos ver, ahora la instancia de TxtLog la recibe el constructor de la clase ProcesoAutorizacionTarea, por lo que cada cambio que tengamos que realizar sobre dicha instancia, podemos hacerlo en un único lugar. Notemos que en este caso estamos “inyectando” al objeto ProcesoAutorizacionTarea  un objeto TxtLog, es decir su dependencia. Esto se denomina Inyección de Dependencias y sobre este tema hemos hablado en varios post. Bien, resuelto esto, tenemos otro problema, seguimos teniendo un mal diseño, ya que es inmóvil. Supongamos que el día de mañana, se desea utilizar un DbLog o XmlLog. Este cambio podría ser un dolor de cabeza, ya que el proceso depende exclusivamente de TxtLog. Algo similar ocurre si necesitamos realizar el calculo de un proceso “SEMI-AUTOMATICO”? Entonces veamos como corregir este error de diseño. Antes que nada vamos a generar las interfaces ILog e ICalculadorPlazosTarea – esta última es una pobre traducción que se me ocurrió, reconozco que con el tiempo se me hizo complicado definir nombres en español 🙂 -.

Definiendo las interfaces

Definiendo las interfaces

Lo siguiente, generar las clases que lo implementen. Por un lado tendremos TxtLog que implementa ILog y por el otro dos nuevas clases llamadas CalculadorPlazosTareaX CalculadorPlazosTareaY que implementan ICalculadorPlazosTarea. Comencemos con TxtLog:

Redefiniendo TxtLog

Redefiniendo TxtLog

Ahora las nuevas clases “calculadoras de plazos”:

Implementaciones de ICalculadorPlazosTarea

Implementaciones de ICalculadorPlazosTarea

Bien, por último solo resta eliminar las dependencias (o mejor dicho invertirlas) de la clase ProcesoAutorizacionTarea:

Aplicando DIP a la clase ProcesoAutorizacionTarea

Aplicando DIP a la clase ProcesoAutorizacionTarea

Como podemos ver  el punto A del principio estaría resuelto con la intrdocción de ILog, ya que ahora tanto ProcesoAutorizacionTarea como TxtLog dependen de una abstracción (es decir, de ILog). También estaríamos cumpliendo del punto B, ya que por ejemplo, los métodos para calcular plazos dependen de la abstracción ICalculadorPlazosTarea y no de la clase ProcesoAutorizacionTarea. Espero que les sea de utilidad!

Gof – Singleton

Vamos a arrancar este 2012 retomando el tema de los patrones de diseño y en este primer post del año veremos el patrón de creación Singleton. La finalidad de Singleton es que solo se pueda crear una única instancia de una clase determinada. De esta forma nos aseguramos que nadie pueda crear instancias adicionales de la clase y que todos accedan a la misma instancia.

Veamos la definición de Wikipedia:

El patrón de diseño singleton (instancia única) está diseñado para restringir la creación de objetos pertenecientes a una clase o el valor de un tipo a un único objeto.

¿En qué casos nos puede resultar útil implementar Singleton? En clases que se encargan de controlar el acceso a un recurso único (imaginemos un archivo abierto en modo exclusivo) o cuando necesitamos tener disponible información que es única para el resto de los objetos.

Hecha la introducción, es hora de escribir código e implementar este patrón con C#. Para esto vamos a crear una nueva aplicación de Consola y agregar una clase llamada Singleton (sobre la cual aplicaremos el patrón).

El primer punto es hacer que la propia clase sea responsable de crear la única instancia de si misma. Para esto vamos a definir el constructor con el modificador private – para impedir que otros puedan crear instancias “desde afuera” – y marcar la clase como sealed – para evitar que se herede de la misma -.

Hagamos una pequeña pausa y veamos que nos dice MSDN de este último modificador:

El modificador sealed se puede aplicar a clases, métodos de instancia y propiedades. Una clase sealed no se puede heredar. Un método sellado reemplaza un método en una clase base, pero no se puede reemplazar también en una clase derivada. Cuando se aplica a un método o propiedad, el modificador sealed siempre se debe utilizar conoverride (Referencia de C#).

Bien, en este momento deberíamos tener la clase Singleton definida de la siguiente manera:

Singleton class

Singleton class

El proximo paso es permitir el acceso a la instancia mediante un método o propiedad de clase. Para esto vamos a agregar una propiedad de sólo lectura (la que llamaremos Instance) que sera responsable de crear y devolvernos la única instancia de la clase:

Instance property

Instance property

Como podemos ver, la primera vez que solicitamos una instancia de la clase, la propiedad Instance se encarga de crearla y asignarla a la propiedad privada instance. Finalmente se devuelve la instancia generada.

Para validar que solo existe un única instancia, vamos a escribir un simple test unitario en el cual crearemos dos objetos Singleton y los compararemos para confirmar si realmente son iguales:

Unit test

Unit test

Si ejecutamos el test, veremos que el mismo es correcto ya que ambos objetos apuntan a la misma y única instancia que se creó en memoria de la clase Singleton.

Test passed

Test passed

Pero esto tiene un pequeño problema, no soporta multi-thread. Al momento de validar si existe una instancia creada, lo hacemos preguntando si el valor de la propiedad instance es distinto de null. Si trabajamos en entornos multi-thread podría darse la situación en que múltiples hilos entren al mismo tiempo en dicho if, por lo que se podrían crear varias instancias de Singleton.

Esto podemos solucionarlo de una manera sencilla, generando un bloqueo (lock) dentro de Instance que nos permita sincronizar todos los accesos a la creación del objeto.

Ampliemos sobre lock consultando MSDN:

La instrucción lock permite garantizar que un subproceso no va a entrar en una sección crucial de código mientras otro subproceso ya se encuentre en ella. Si otro subproceso intenta entrar en un código bloqueado, esperará, o se bloqueará, hasta que el objeto se libere.

Lo que necesitamos para hacer esta validación, es crear un objeto del tipo Object que llamaremos sync y que utilizaremos en el bloqueo:

Sync property

Sync property

Si volvemos a correr el test, el mismo vuelve a pasar correctamente pero ahora con soporte multi-thread. 😉

Sin bien ya tenemos implementado el patrón Singleton en nuestra clase, veamos que hay una forma más simple de aplicarlo. Solo debemos definir la propiedad instance como readOnly. Este modificador nos va a permitir crear la instancia en la misma declaración de la propiedad evitando que se creen nuevas instancias luego.

Veamos de que se trata este modificador consultando una vez más MSDN:

La palabra clave readonly corresponde a un modificador que se puede utilizar en campos. Cuando una declaración de campo incluye un modificador readonly, las asignaciones a los campos que aparecen en la declaración sólo pueden tener lugar en la propia declaración o en un constructor de la misma clase.

Refactorizamos y obtendremos una nueva y más prolija clase Singleton 🙂

Refactorizando Singleton

Refactorizando Singleton

Cabe aclarar que el comportamiento es exactamente el mismo que el de la implementación del punto anterior.

Espero que les sea útil y tengan presente el patrón para, llegado el caso, decidan implementarlo.

ASP.NET MVC 3 y Structure Map

Como ya hemos hablado en otros post, una de las características que nos permiten mejorar el código de nuestros desarrollos es la utilización de patrones de diseño. Uno de esos patrones es la Inyección de Dependencias que consiste en suministrar objetos a una clase en lugar de ser la propia clase quien cree el objeto.

En este post voy a mostrarles un framework llamado Structure Map que nos permite aplicar este patrón en nuestros proyectos .NET y la forma de integrarlo al soporte de IoC que provee ASP.NET MVC 3.

Structure Map es un framework para .NET que nos permite trabajar con Inyección de Dependencias e Inversión de Control.

El primer paso sera agregar las referencias a la librería de Structure Map. Para ello vamos a instalarlas utilizando Nuget:

Agregando la librería Structure Map

Agregando la librería Structure Map

Es importante recordar que la pieza central respecto al soporte de IoC que provee ASP.NET MVC 3 es la interfaz IDependencyResolver que nos permite “conectar” distintos motores de IoC con tan solo implementarla en una clase y luego registrarla.

En nuestro ejemplo vamos a crear nuestro propio Dependecy Resolver que va a ser manejado con Structure Map. Para ellos vamos a crear la clase StructureMapDependencyResolver que, como dijimos anteriormente, implementará IDependencyResolver:

1: public class StructureMapDependencyResolver : IDependencyResolver
2: {
3:     private IContainer container;
4:
5:     public StructureMapDependencyResolver(IContainer container)
6:     {
7:         this.container = container;
8:     }
9:
10:     public object GetService(Type serviceType)
11:     {
12:         if (serviceType.IsAbstract || serviceType.IsInterface)
13:         {
14:             return this.container.TryGetInstance(serviceType);
15:         }
16:         else
17:         {
18:             return this.container.GetInstance(serviceType);
19:         }
20:     }
21:
22:     public IEnumerable<object> GetServices(Type serviceType)
23:     {
24:         return this.container.GetAllInstances<object>()
25:             .Where(s => s.GetType() == serviceType);
26:     }
27: }

Lo que hicimos aquí fue implementar los siguiente métodos:

En ambos métodos hemos utilizado el contedor IContainer (que nos provee la librería Structure Map) cuya finalidad es la de registrar y resolver las dependencias que hayamos definido.

Bien, ahora vamos a crear una interfaz bien simple, la cual denominaremos IMessage:
1: public interface IMessage
2: {
3:     string GetMessage();
4: }

Y dos implementaciones bien sencillas de la misma, la primera la llamaremos HelloMessage:

1: public class HelloMessage : IMessage
2: {
3:     public string GetMessage()
4:     {
5:         return "Buenos días!" ;
6:     }
7: }

Y la segunda ByeMessage:

1: public class ByeMessage : IMessage
2: {
3:     public string GetMessage()
4:     {
5:         return "Hasta mañana!" ;
6:     }
7: }

Llego el momento de registrar a nuestro “resolvedor(?) de dependencias”  🙂 y para ello debemos agregar las siguientes lineas en el archivo global.asax:

1: public class MvcApplication : System.Web.HttpApplication
2: {
3:     public static void RegisterGlobalFilters(GlobalFilterCollection filters)
4:     {
5:         filters.Add(new HandleErrorAttribute());
6:     }
7:
8:     public static void RegisterRoutes(RouteCollection routes)
9:     {
10:         routes.IgnoreRoute("{resource}.axd/{*pathInfo}" );
11:
12:         routes.MapRoute(
13:             "Default", // Route name
14:             "{controller}/{action}/{id}", // URL with parameters
15:             new { controller = "Home" , action = "Index" } 
16:         );
17:
18:     }
19:
20:     protected void Application_Start()
21:     {
22:         AreaRegistration .RegisterAllAreas();
23:
24:         RegisterGlobalFilters(GlobalFilters.Filters);
25:         RegisterRoutes(RouteTable.Routes);
26:
27:         InitializeContainer();
28:     }
29:
30:     public void InitializeContainer()
31:     {
32:         var c = InitContainer();
33:         DependencyResolver.SetResolver(new StructureMapDependencyResolver(c));
34:     }
35:
36:     private static IContainer InitContainer()
37:     {
38:         var container = new Container();
39:
40:         container.Configure(s => s.For<IMessage>().Use<HelloMessage>());
41:
42:         return container;
43:     }
44: }

Dentro del método Application_Start() invocamos un nuevo método llamado InitializeContainer() en el cual vamos a registrar StructureMapDependencyResolver como el encargado de resolver las dependencias. Previamente a este paso hemos creamos el método InitContainer() en el cual hemos creado un objeto Container al cual le fuimos configurando el mapeo entre interfaces y clases que sera utilizado para poder resolver las dependencias (en el ejemplo solo es uno y es el de IMessage).

Es hora de crear el controlador HomeController cuyo constructor recibirá como parámetro un IMessage el cual será “inyectado” cada vez que sea instanciado (de lo cual ya no debemos preocuparnos gracias a IDependencyResolver). También vamos a definir un método de acción Index:

1: public class HomeController : Controller
2: {
3:     private IMessage MessageService { get ; set ; }
4:
5:     public HomeController(IMessage service)
6:     {
7:         this .MessageService = service;
8:     }
9:
10:     public ActionResult Index()
11:     {
12:         ViewBag.Message = MessageService.GetMessage();
13:
14:         return View();
15:     }
16: }

Ejecutamos y veremos que el servicio instancia es el que hemos definido al momento de crear el contenedor, en este caso HelloMessage:

Resultado obtenido de HelloMessage

Resultado obtenido de HelloMessage

Ahora modificamos el contenedor para instanciar el servicio ByeMessage en la interfaces IMessage:

1: private static IContainer InitContainer()
2: {
3:     var container = new Container();
4:
5:     container.Configure(s => s.For<IMessage>().Use<ByeMessage>());
6:
7:     return container;
8: }

Ejecutamos y verificamos que se invoco el servicio que definimos:

Resultado obtenido con ByeMessage

Resultado obtenido con ByeMessage

De más esta decir, que esto simplifica y mejora el código de nuestros desarrollos, ya que en el caso de que debamos modificar la referencias a nuestros servicios, y si estos mantienen el contrato, solo debemos modificar una sola línea de nuestro proyecto, y es el mapeo entre interfaz y clase que hemos configurado en el contenedor de dependencias.

Quienes quieran descargar el proyecto pueden hacerlo desde acá: MvcAndStructureMap.rar.

Espero que les sea de utilidad.

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.