Patrones de Desarrollo Software – I

Bienvenidos de nuevo! En esta ocasión os traigo un conglomerado de algunos principios y patrones de diseño de software que considero que son de verdadera utilidad a la hora de programar nuestro código y que éste quede de una forma elegante y concisa.

Todos y absolutamente todos los patrones de desarrollo que existen presentan o han presentado alguna utilidad, y en éste caso utilizo el pasado debido a que existen algunos patrones que a causa del nacimiento de nuevas técnicas o herramientas complementarias a nuestro código se han ido quedando un poco obsoletos. Aun así nunca está de más conocerlos, ya que nunca se sabe en qué situación te puedes encontrar donde tengas que recurrir a todo tu arsenal para afrontar el problema, y en consecuencia cuantas más herramientas tengamos en nuestro cajón mayor versatilidad tendremos a la hora de abordar el problema.

Por si alguno aún no lo sabe, un patrón de diseño de software viene a ser una solución concreta, a nivel de código, a un problema determinado. Normalmente es una solución reusable, en el sentido de que en el mundo de la programación existen una serie de problemas comunes de los que antiguamente ya se han enfrentado decenas de miles de personajes, y entre ellos de la talla de Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides, o también conocidos como ‘La Banda de los Cuatro’ (Gang of Four).

Pues bien, estos 4 ingenieros del software recopilaron las soluciones a dichos problemas en un libro denominado Design Patterns: Elements of Reusable Object-Oriented Software, que recomiendo encarecidamente a todo el mundo. Es posible que si leas el libro algunas de las soluciones presentadas nunca veras la necesidad o forma de utilizarlas, eso se debe a que el problema que soluciona es quizá demasiado específico, y es por esto que en la tarea diaria del programador no tenga cabida. Por esto vengo a haceros un pequeño resumen y a mostraros algunas de estas soluciones que si que he ido aplicando o encontrado la necesidad de utilizar en mi día a día.

Antes de empezar también he de decir que no sólo utilizaré los patrones de diseño que aparecen en el libro que antes mencionaba, ya que patrones de diseño de software existen miles, ahora bien, estas 4 personas se encargaron de recopilar allá en su momento aquellos que realmente veían una constante necesidad de aplicación. El mundo evoluciona y el código no es menos, puesto que con el nacimiento de nuevos lenguajes, frameworks y tecnologías, vienen de la mano de nuevos estándares de soluciones a distintos problemas comunes.

También he de destacar que todos los ejemplos de código que mostraré en este artículo se encuentran en el repositorio https://github.com/xXjoakinXx/MindBodyAndCode2019 en concreto en el proyecto DesignPatterns. Se trata de una aplicación de consola de Net Core mostrando la ejecución de los distintos patrones en si.

Patrón Método Factoría

Comenzamos con el patrón Método Factoría, uno de los patrones creacionales que se encuentran en el libro de patrones de diseño de ‘La Banda de los Cuatro’ antes mencionado. Se considera un patrón creacional puesto que su principal cometido es el de crear o instanciar objetos de nuestro negocio, y en éste caso también cumple con la necesidad de tener que crear en tiempo de ejecución distintos tipos de clases de forma dinámica.

Este patrón es comúnmente usado puesto que estás delegando todo el proceso complejo de creación a un ente específico encargado única y exclusivamente de dicha funcionalidad. Por poner un ejemplo imaginemos el siguiente caso:

Como podemos ver en nuestro modelo disponemos de dos entidades que son las que queremos instanciar, Car y MotorBike, y ambas clases implementan la interfaz de IVehicle, indicando que ambas representan el mismo tipo de ente que en nuestro caso sera el tipo vehículo. Para nuestra definición de lo que representa un vehículo he hecho que la interfaz IVehicle obligue a las clases que la implementen a definir una propiedad entera denominada WheelsNumber (Número de ruedas del vehículo) y un método que devolverá una cadena de texto informando del hecho de que el vehículo está en movimiento drive().

Pero realmente la implementación del patrón método factoría se encuentra en la entidad VehicleFactory, que como su propio nombre indica, será la encarga de crear según el tipo indicado por parámetro un vehículo u otro. Para dar un poco más de luz al asunto veamos el código:

   public interface IFactory
   {
       IVehicle Create(Type type);
   }

    public class VehicleFactory : IFactory
    {
        public IVehicle Create(Type type)
        {
            if (type == typeof(Car))
                return new Car();

            if (type == typeof(MotorBike))
                return new MotorBike();

            return null;
        }
    }
    public interface IVehicle
    {
        public int WheelsNumber { get; }

        public string drive();
    }

    public class Car : IVehicle
    {
        public int WheelsNumber => 4;

        public string drive()
        {
            return "Im driving in a car";
        }
    }

    public class MotorBike : IVehicle
    {
        public int WheelsNumber => 2;

        public string drive()
        {
            return "Im on a MotorBike";
        }
    }

Con esto conseguimos abstraer del proceso de creación de nuestras clases en el modelo, centralizandolo en otra entidad y permitiendo obtener el resultado de forma dinámica en tiempo de ejecución. Un ejemplo de uso del método factoría podría ser el siguiente:

var vehicleFactory = new VehicleFactory();

var vehicle = vehicleFactory.Create(typeof(Car));
var otherVehicle = vehicleFactory.Create(typeof(MotorBike));

La ventaja de aplicar éste patrón es la de no tener que exponer cómo tienen que crearse nuestras clases, esa acción se la delegamos a la factoría. Permitiendo además poder escoger qué tipo de clase quiero crear y olvidarme de cómo debería de crear mis entidades del modelo.

Patrón Singleton

Este patrón es uno de los más sencillos de implementar y además también se encuentra dentro del famoso libro de Gang of Four. Básicamente con éste patrón de lo que nos estamos asegurando es de que al instanciar una clase, tan solo es posible crear una única instancia de la misma. Y por lo tanto es posible ofrecerle un punto de acceso global hacía la misma. Si observamos su diagrama UML de implemetación:

Se aprecia la sencillez del mismo, pues únicamente con la misma clase de la que queremos asegurarnos su instancia única nos valemos para implementarlo. Para poder apreciar tal sencillez miremos dentro del código:

    public class SingletonCar : IVehicle
    {
        private SingletonCar() { }

        private static SingletonCar _instance;

        public static SingletonCar GetInstance
        {
            get
            {
                if (_instance is null)
                    _instance = new SingletonCar();

                return _instance;
            }
        }

        public int WheelsNumber => 4;

        public string drive()
        {
            return "Im driving in a unique car";
        }
    }

Tan solo necesitamos proteger el constructor de la clase estableciéndolo como privado, evitando que pueda ser creada desde otra clase, y ofreciendo un método o una propiedad que nos aplique la lógica que nos asegure una sola instancia de la misma. Comprobemos el funcionamiento con el siguiente ejemplo:

var carSingleton = SingletonCar.GetInstance;
Console.WriteLine("Hi! Im singleton car with Id: " + carSingleton.GetHashCode());

var otherCarSingleton = SingletonCar.GetInstance;
Console.WriteLine("Hi! Im singleton car with Id: " + otherCarSingleton.GetHashCode());

Y el resultado del código de ejemplo anterior sería el siguiente:

Comprobamos como el resultado del método GetHashCode() nos devuelve el mismo Hash para dos llamadas a la creación de la instancia. El principal objetivo de éste patrón es la capacidad de poder controlar el acceso a un objeto único de nuestro código, del que no queremos crear varias instancias.

Patrón Builder

Seguimos con otro patrón de creación que también se encuentra en el libro de Gang of Four, en éste caso tratamos con el denominado patrón constructor o Builder. Este patrón es usado principalmente a la hora de crear objetos complejos de nuestro modelo, donde los parámetros para poder ser creados son variables y por tanto desde el exterior habrá situaciones en las que el objeto se construya de una forma distinta para diversas situaciones. Es por ésto por lo que al crear el objeto nos centraremos en construir las partes que lo componen que el objeto global en su conjunto. Su diagrama UML de ejemplo sería como el siguiente:

A fin de cuentas disponemos de una interfaz «ICarBuilder» en este caso, donde son definidos todos los pasos que son necesarios para construir el objeto Car. A partir de ésta interfaz intervienen las clases concretas constructoras GolfBuilder y HondaBuilder, encargadas de construir un tipo de coche, donde por el contexto podemos deducir que GolfBuilder creará instancias de un modelo de coche tipo Golf, y por el contrario HondaBuilder hará lo propio con el modelo de coche Honda. Estas clases necesitan por lo tanto definir la forma de construir la entidad en base a los métodos propiciados por la interfaz.

Veamos ahora el código para que se aprecie realmente cómo funciona este patrón:

    public class Car : IVehicle
    {
        public string Enrollment { get; set; }

        public double HorsePower { get; set; }

        public DateTime EnrollmentDate { get; set; }

        public int WheelsNumber => 4;

        public string drive()
        {
            return "Im driving in a car";
        }

        public override string ToString()
        {
            return $"Car with {nameof(Enrollment)}: {Enrollment}, {nameof(HorsePower)}: 
                    {HorsePower}, {nameof(EnrollmentDate)}:{EnrollmentDate}";
        }
    }

Antes de nada os muestro cómo he extendido las propiedades de la clase Car, para ahora incluir más información a ser construida por los Builders, como son las propiedades Enrollment, HorsePower y EnrollmentDate.

    public interface ICarBuilder
    {
        ICarBuilder SetEnrollment(string enrollment);
        public ICarBuilder SetHorsePower();
        public ICarBuilder SetEnrollmentDate(DateTime enrollmentDate);
        public Car Build();
    }

Interfaz constructora para instancias de tipo Car, como he comentado antes contiene los pasos necesarios para construir la entidad.

    public class GolfBuilder : ICarBuilder
    {
        private Car _car;

        public GolfBuilder()
        {
            _car = new Car();
        }

        public ICarBuilder SetEnrollment(string enrollment)
        {
            _car.Enrollment = enrollment;
            return this;
        }

        public ICarBuilder SetHorsePower()
        {
            _car.HorsePower = 170;
            return this;
        }

        public ICarBuilder SetEnrollmentDate(DateTime enrollmentDate)
        {
            _car.EnrollmentDate = enrollmentDate;
            return this;
        }

        public Car Build()
        {
            return _car;
        }
    }
    public class HondaBuilder : ICarBuilder
    {
        private Car _car;

        public HondaBuilder()
        {
            _car = new Car();
        }

        public ICarBuilder SetEnrollment(string enrollment)
        {
            _car.Enrollment = enrollment;
            return this;
        }

        public ICarBuilder SetEnrollmentDate(DateTime enrollmentDate)
        {
            _car.EnrollmentDate = enrollmentDate;
            return this;
        }

        public ICarBuilder SetHorsePower()
        {
            _car.HorsePower = 150;
            return this;
        }

        public Car Build()
        {
            return _car;
        }
    }

Y aquí tenemos a los constructores específicos, encargados de construir instancias de un modelo distinto cada uno. Como se puede apreciar ambas implementaciones en el ejemplo son muy parecidas, menos el método SetHorsePower() donde para cada modelo de vehículo le estamos estableciendo unos caballos de potencia distintos. Y el método Build() con el que finalizamos la construcción de la entidad y la retornamos, así podríamos dinámicamente construir las entidades con distintos parámetros de una forma simple y elegante, como vemos a continuación en su ejemplo de uso:

  var golf = new GolfBuilder()
    .SetEnrollment("3685PKV")
    .SetHorsePower()
    .SetEnrollmentDate(DateTime.Now)
    .Build();

  var honda = new HondaBuilder()
    .SetEnrollment("4367TDL")
    .SetHorsePower()
    .SetEnrollmentDate(DateTime.Now)
    .Build();

  Console.WriteLine(golf.ToString());
  Console.WriteLine(honda.ToString());

Donde el resultado del código anterior es el siguiente:

Con éste ejemplo vemos cómo conseguimos dinamismo a la hora de crear instancias de objetos donde realmente importa más cómo se ensambla dicho objeto en distintas partes, además de esto es conveniente recordar que éste patrón solo funciona si el ensamblado de las partes no requiere de ningún orden previo, es decir, nada me debe impedir modificar el orden en el que ejecuto los métodos de asignación de propiedades. Pero eso sí, siempre debe de terminar con un método de construcción final como puede ser el método Build().

Patrón Inyección de Dependencias

Este siguiente patrón que mostraré a continuación es totalmente distinto de los anteriores, pero aun así sirve para gestionar de forma aún más flexible, modularizada e independiente la creación de los objetos o servicios que utilizaremos en nuestro código. Este patrón sería el encargado de suministrar de forma global todas las dependencias que nuestro código requerirá a lo largo de su ciclo de vida, ofreciendolas siempre en el momento en el que se necesitan, y permitiendo además configurar el ciclo de vida de las mismas.

Pero antes de nada repasemos algunos conceptos. Partiremos del concepto de dependencia en programación:

Básicamente podríamos decir que si una clase A para cumplir sus objetivos o funcionalidad en algún momento va a necesitar de otra clase B, y por lo tanto tiene una referencia a ésta (Flecha verde de la imagen), en ese momento estaríamos creando una dependencia entre A y B. Pero esto aún no ha terminado porque lo único que la clase A conoce, es que necesita de otra clase B para funcionar, pero no sabe (o por lo menos no debería de saber) de donde proviene ésta otra clase o quién debería de suministrarsela para poder funcionar. Así que, el proceso de creación y posterior distribución del objeto B en A es lo que consideraríamos el patrón Inyección de Dependencias.

Este tercer ente ubicado entre A y B es el que denominaremos Contenedor de Inyección de Dependencias, llamado así debido a que generalmente son los que registran cómo deben de crearse los objetos de nuestro sistema, con la configuración de su ciclo de vida inclusive. Y así tan sólo tendremos que «pedirle» (Cuando hablo de petición sigo refiriéndome a una dependencia) al contenedor que nos genere una nueva instancia de un objeto en concreto y éste nos lo proporcionará, y no todo termina ahí, puesto que esta acción es prácticamente invisible al programador. Vemos a continuación las distintas formas de petición que existen:

Nota: Para los ejemplos de código que mostraré a continuación yo he usado Autofac como contenedor de inyección de dependencias, dado que me siento más cómodo programando en C# y este contenedor es el que mejor conozco. Pero existe una gran cantidad de contenedores y en distintos lenguajes, como Ninject, PHP-DI, Unity Container …etc. Todos sirven para la misma finalidad, por tanto, lo único que cambiaría seria el lenguaje y las peculiaridades al registrar elementos de cada contenedor.

Inyección por constructor

La dependencia es proporcionada mediante el constructor de la clase que la necesita. Ejemplo:

    public class InjectionExample
    {
        private readonly IFactory _vehicleFactory;

        public MotorBike MotorBike { get; set; }

        public InjectionExample(IFactory vehicleFactory)
        {
            _vehicleFactory = vehicleFactory ?? throw new 
                     System.ArgumentNullException(nameof(vehicleFactory));
        }

        public IVehicle GetCar()
        {
            return _vehicleFactory.Create(typeof(Car));
        }
    }

Usando esta clase InjectionExample, apreciamos cómo tiene dos dependencias, una con la interfaz IFactory; que si recordamos de ejemplos anteriores esta es usada en la implementación del patrón Método Factoría, y otra dependencia en la propiedad de tipo MotorBike. En esta ocasión nos centraremos en la dependencia con la interfaz IFactory, que como podemos apreciar ésta dependencia es proporcionada en el constructor de la clase InjectionExample, con lo que la dependencia será resuelta en el momento en que ésta sea instanciada. Pero ¿Como registramos tal dependencia con el contenedor de Autofac? Lo veremos con el ejemplo siguiente:

    public class AutofacModule : Module
    {
        public AutofacModule()
        {

        }

        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<VehicleFactory>().As<IFactory>().SingleInstance();

            builder.RegisterType<InjectionExample>()
                .InstancePerDependency();
        }
    }

Esta clase es la implementacion que sigue Autofac para generar Módulos, un módulo viene a ser, cómo su propio nombre indica, un conjunto de instrucciónes de registro en el contenedor de Inyección de Dependencias relacionados entre sí o que deberían de registrarse en conjunto.

Pero lo importante a destacar aquí es que en el método «Load()» del módulo es donde se realizan los registros, y como vemos para que la inyección por constructor funcione tan sólo basta con registrar ambas clases, y Autofac sabrá en tiempo de ejecución resolverlas automáticamente. Entrando un poco más en detalle vemos que el registro de la factoría de vehículos se ha realizado indicando que el registro del tipo VehicleFactory se asocia con la interfaz IFactory, esto quiere decir que allí donde se encuentre la dependencia de IFactory, el contenedor debe resolverla con una instancia de VehicleFactory. Además se le ha establecido como ciclo de vida a éste registro de SingleInstance(), indicando que siempre que resuelva la dependencia será con la misma instancia, sin crear una nueva (¿No os suena de otro patrón previamente visto…?)

En cambio el registro de la clase InjectionExample es más sencillo aún, ya que tan solo registramos mediante el tipo esta misma clase, dejando el ciclo de vida como InstancePerDependency(), queriendo decir que genere una instancia nueva por cada dependencia.

Ahora si vemos un ejemplo de uso de éste código con el contenedor de Autofac, sería parecido al siguiente:

 var containerBuilder = new ContainerBuilder();
 containerBuilder.RegisterModule<AutofacModule>();
 var diContainer = containerBuilder.Build();

 // Constructor injection example
 var injectionExample = diContainer.Resolve<InjectionExample>();
 var car = injectionExample.GetCar();
 Console.WriteLine(car.drive());

Los pasos son simples, se instancia el contenedor de Inyección de Dependencias, se registra el módulo o módulos creados, finalizamos la creación del contenedor y pasamos a resolver la instancia de tipo InjectionExample. Seguidamente vemos cómo usamos directamente el método GetCar() para obtener una instancia de tipo Car, a la vista está la sencillez de la resolución de dependencias en éste punto. Para el que no se haya percatado, en el momento en el que hemos resuelto la clase InjectionExample a través del contenedor, éste mismo ha encontrado la clase y ha resuelto la dependencia establecida en su constructor con la entidad VehicleFactory, donde posteriormente se usa en el método GetCar() para crear una instancia del tipo indicado.

Inyección por propiedades

Esta dependencia se ofrece a través de la misma propiedad, por lo que tendremos que asegurarnos que la propiedad puede ser sobre-escrita desde fuera por el contenedor de inyección de dependencias. Ejemplo:

    public class AutofacModule : Module
    {
        public AutofacModule()
        {

        }

        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<VehicleFactory>().As<IFactory>().SingleInstance();

            builder.RegisterType<MotorBike>().InstancePerDependency();

            builder.RegisterType<InjectionExample>()
                .PropertiesAutowired()
                .InstancePerDependency();
        }
    }

En éste caso vemos cómo se ha extendido el módulo de Autofac también usado en el ejemplo anterior. Y tan sólo vemos que se ha añadido el registro de la clase MotorBike, y en el registro de InjectionExample, se ha añadido la llamada al método PropertiesAutoWired(). Con ésta llamada estamos indicando al contenedor que debe resolver de forma automática las dependencias en las propiedades de la clase (Existen varias formas de resolver las dependencias por propiedades, ésta sería de las más sencillas usando Autofac). Y si recordamos la implementación de InjectionExample, ésta misma tenía una propiedad de tipo MotorBike, con lo que en tiempo de ejecución sabrá resolverla. Veamos el ejemplo:

 var containerBuilder = new ContainerBuilder();
 containerBuilder.RegisterModule<AutofacModule>();
 var diContainer = containerBuilder.Build();

 // Property injection example
 var injectionExample = diContainer.Resolve<InjectionExample>();
 var motorBike = injectionExample.MotorBike;
 Console.WriteLine(motorBike.drive());

Así vemos cómo la resolución de la misma instancia de InjectionExample, nos permite acceder directamente a la propiedad MotorBike, resuelta ésta automáticamente por el contenedor.

Inyección por método

Básicamente la resolución de la dependencia se realiza a través de un método disponible desde la misma clase. Los parámetros del método deben de ser los objetos inyectables. Ejemplo:

    public class Truck : IVehicle
    {
        public int WheelsNumber => 4;

        public string drive()
        {
            return "Driving a Truck!";
        }
    }

    public class MethodInjectionExample
    {
        private Truck _truk;

        public void SetTruck(Truck truck)
        {
            _truk = truck;
        }

        public Truck GetTruck()
        {
            return _truk;
        }
    }

Ahora entran en juego la implementación de un tercer tipo de vehículo, representado éste con la clase Truck y otra clase denominada MethodInjectionExample, en la que vemos que presenta claramente una dependencia con la clase Truck, pero ésta no es a través de constructor ni de propiedad. En éste caso será una dependencia resuelta a nivel de método, y para ello veamos cómo se han registrado en Autofac:

    public class AutofacModule : Module
    {
        public AutofacModule()
        {

        }

        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<VehicleFactory>().As<IFactory>().SingleInstance();

            builder.RegisterType<MotorBike>().InstancePerDependency();
            builder.RegisterType<Truck>().InstancePerDependency();

            builder.RegisterType<InjectionExample>()
                .PropertiesAutowired()
                .InstancePerDependency();

            builder.Register(c =>
            {
                var methodInjectionExample = new MethodInjectionExample();
                var truck = c.Resolve<Truck>();
                methodInjectionExample.SetTruck(truck);
                return methodInjectionExample;
            });
        }
    }

Vemos en este caso como el módulo se ha extendido con el registro de la clase Truck; con su forma simple anteriormente vista, y el registro de la clase MethodInjectionExample; el cual, en su registro vemos como se ha tenido que manualmente resolver la dependencia con Truck, y asignarla con el método SetTruck(truck). Si vemos el ejemplo de su uso:

 var containerBuilder = new ContainerBuilder();
 containerBuilder.RegisterModule<AutofacModule>();
 var diContainer = containerBuilder.Build();

 // Method injection example
 var methodInjectionExample = diContainer.Resolve<MethodInjectionExample>();
 var truck = methodInjectionExample.GetTruck();
 Console.WriteLine(truck.drive());

La implementación de su uso no cambia en nada especial a las anteriores, tan solo hay que tener en cuenta que la resolución de la dependencia se resuelve internamente a través de un método de la propia clase.

Así finaliza un breve ejemplo de los distintos tipos de uso del patrón Inyección de Dependencias, en éste caso el ejemplo es algo más extenso ya que la complejidad del mismo patrón lo requiere. Y no sólo termina aquí, puesto que con éste tipo de contenedores conseguimos una maniobrabilidad a la hora de programar increíble, además de conseguir que nuestras aplicaciones sigan el principio de Inversión de Control (IoC), donde en pocas palabras éste principio nos dice que se invierte el control de nuestras aplicaciones dejandoselo a un ente externo (Contenedores de Inyección de Dependencias). Traducido rápidamente viene a decir que no tenemos, los programadores, porqué pelearnos con la gestión y creación de objetos de nuestro sistema, que deleguemos esta tarea a los contenedores y nosotros tan sólo necesitamos configurarlos.

Con ésto conseguimos las siguientes ventajas:

  • Ayuda al testear nuestras aplicaciones, puedes cambiar fácilmente las dependencias por objetos Mock.
  • Nuestra aplicación se vuelve más legible y fácil de seguir, se reduce mucho el código y éste se puede localizar con mayor facilidad.
  • Habilita un bajo acoplamiento, siguiendo la buena práctica de exponer interfaces en lugar de clases concretas a través de los contenedores.

Como he dicho anteriormente con los contenedores de Inyección de Dependecias (O también llamados de Inversión de Control) y en concreto con Autofac, tan sólo he mostrado la punta del iceberg, existen miles de configuraciones y herramientas que nos ofrecen estos componentes, y aquí os dejo el enlace a la documentación oficial de Autofac para todo aquel que quiera seguir investigando en éste tema: https://autofaccn.readthedocs.io/en/latest/

Conclusión

Con éste conjunto de patrones terminamos esta primera sesión de Patrones de Desarrollo Software. Todos los patrones que al final se han mostrado están más orientados a la creación de objetos, pero existen muchos otros tipos de patrones (Estructurales y de comportamiento) que ya iremos vislumbrando en posteriores artículos.

Como habréis apreciado no sigo ningún orden en concreto, tan solo muestro aquellos que considero de especial utilidad y que por lo tanto a lo largo de mi día a día he tenido que utilizarlos en alguna que otra ocasión. Espero que os haya gustado y ya sabeis, a seguir programando!!