martes, 13 de octubre de 2015

Patrón de Diseño - Decorator


Introducción


Los patrones de diseño es algo que leí hace tiempo, he usado algunos en los proyectos en los que trabaje y en los que trabajo, pero una forma de asimilarlos, creo yo, es practicar con ellos con ejemplos y por eso este post. Con este post,intentaré implementar un patrón de diseño pero de forma que fuese como parte de algo real. Digo esto por que hay algunos libros en los que ponen los ejemplos con nombres de clases como "classA" "parrentClassA". Aunque puede ser explicativo, a mí se me queda mejor con nombres más concretos.

Y como hay mucha, mucha información, donde explican este tipo de patrón, dejo más que nada, la fuente original.

"Design Patterns: Elements of Reusable Object-Oriented Software"




Hace tiempo estuve trabajando en una empresa en la que por pantalla visualizaba viajes de buses, y pasando el cursor, mostraba informacón sobre el bus.

He tomado este caso para hacer un ejemplo del patrón Decorator.

Patrón Decorator


En este ejemplo, tenemos unos autobuses (Bus.cs) los cuales los conduce un conductor asociado al Bus(DriverInfo.cs). Y unos de los requisitos es que cuando pasa el cursor por el bus dibujado en pantalla, que se muestre la informacón asociada al conductor. En este caso, para complicar un poco más la cosa, se muestra dos informaciones del conductor:
- Información del nombre del conductor (Texto) (DecoratorInfoDriver.cs)
- Gráfica de las horas conducidas (Gráfico) (DecoratorGraphicsDriver.cs)

Aquí, abajo se muestra el diagrama de clases.


Comentarios


En el patrón de diseño decorator, se ve que los objetos que decoran el objeto, tiene el mismo interfaz o métodos que
el objeto que decora, para que de esta forma, los objetos clientes, no 'noten' la diferencia y traten a ambos como iguales.

Pero luego, como los objetos se les añade funcionalidad a través de añadir decorator, pero ¿y a la hora de quitarla?. A la hora de quitarla, sería, supongo, quitar los objetos decoradores, pero en este ejemplo, se quitan todos los decorados a la vez. La forma en que se quita un objeto un decorator es de la forma.

//borrar de la lista de _buses todos los objetos decorators (los que muestran la información del conductor)
private void removeInfoDriverInfo()
{
    for (int i = 0; i < _buses.Count; i++)
    {
        DecoratorInfo decoInfo = _buses[i] as DecoratorInfo;

        //Si hay en la lista algún objeto decorado..
        if (decoInfo != null)
        {
            //obtener el objeto que esta siendo decorado
            Bus bus = decoInfo.GetVehicule() as Bus;
            _buses.Remove(decoInfo); //borrar el objeto decorado de la lista de visualizaicon
            _buses.Add(bus); //añadir a la lista de visualiacion el objeto que era decorado
        }
    }
}

El método removeInfoDriverInfo, en runtime, va añadiendo y borrando objetos a la lista de visualización y de está forma, se añade o se borran las funcionalidades de cada objeto.

Luego el método "GetVehicule", retorna le objeto que esta siendo decorado. No estoy seguro, si me estoy ciñendo a la teoría, pero al acceder al objeto que esta siendo decorado, era necesario, para incluirlo dentro de la lista de visualización (List<ControlsUI.VehicleUI> _buses), y una forma de obtener el objeto decorado através de sus decoradores era con el método GetVehicule() (también se podría haber llamado GetDecoratedObject(), pero como estamos dentro del dominio del problema de los buses...)

public VehicleUI GetVehicule()
{
    if (_vehicle is DecoratorInfo)
    {
        DecoratorInfo decoratorInfo = (DecoratorInfo)_vehicle;
        return decoratorInfo.GetVehicule();
    }
    else
    {
        return _vehicle;
    }
}


Conclusión

La conclusión, una que por lo menos saco, es que si no lo hubise implentado, no me hubiese encontrado con el problema de como recuperar el objeto decorado, para quitar de esta forma, la funcionalidad añadida que le dan los objetos decoradores.

Habrá muchas formas, pero la que he escogido para simplificar, es que en a lista  List<ControlsUI.VehicleUI> _buses, estén todos los objetos, tanto Bus como  ecoratorGraphicsDriver y/o DecoratorInfoDriver, si se van borrando y añadiendo objetos a esta lista, a la par que quiera el usurio que un Bus tenga una funcionalidad añadida (ver info del conductor) o no.

Y creo que lo mismo ha sido un ejemplo, muy sencillo, pero ha sido una forma de verlo un poco más, y de no solo leerlo.

El código fuente se puede descargar aquí.

Félix Romo
felix.romo.sanchezseco@gmail.com