Principios SOLID e inyección de dependencias

El pasado jueves 27 tuve la oportunidad de asistir a la reunión mensual del grupo de usuarios .Net de Madrid (Mad.nug) y la conversación estuvo enfocada a la arquitectura de software, inyección de dependencias, inversión de control y algo de principios SOLID, algo de lo que era bastante ajeno.

Este tema de conversación me pareció bastante interesante y he estado indagando, lo que me ha llevado a escribir a modo de artículo, algunas ideas que he adquirido de estos temas.

En este artículo se verá un resumen los principios SOLID, luego se hablará de inyección de dependencias, y finalmente un poco de Unity, un framework de inyección de dependencias creado por el equipo de Microsoft Patterns and Practices.

Principios SOLID

Son principios de diseño básicos para poder crear aplicaciones robustas y sostenibles, creados por Robert C.Martin, y es, como define Hadi Hariri en el vídeo que se enlaza al final del artículo, un acrónimo de acrónimos, es decir, una combinación de varios principios ya existentes.

  • S-RP: Single Responsability Principle: Especifica que una clase (o un método) solamente tiene que tener un propósito concreto, es decir, tener una única responsabilidad o un motivo por el que cambiar. Esto se reduce en clases más pequeñas y más fáciles de entender.
  • O-CP: Open Closed Principle: Se basa en delegar la responsabilidad a la clase. Si existen una serie de acciones que dependen del subtipo de una clase, es más fácil que esa funcionalidad esté implítica en la clase, y que las subclases las reimplementen (o las especifiquen) mediante polimorfismo, una de las características más comunes de los lenguajes orientados a objetos. Esto se resume en que una clase debe estar abierta a extensión y cerrada a cambios.
  • L-SP: Liskov Susbtitution Principle: Explica que todas las clases que hereden de una superclase, por decirlo así, no deben replicar funcionalidad ya implementada en esta clase, con lo cual la clase base se deberá poder sustituir por las subclases en cualquier región del código.
  • I-SP: Interface Segregation Principle: Propone dividir las interfaces en algo que tenga más sentido, en vez de dejar operaciones de la interfaz por implementar, en cierta manera sería una aplicación del SRP a las interfaces, teneniendo en cuenta que una clase puede implementar varias interfaces de manera simultánea. No se debe forzar a clientes a implementar métodos no necesarios.
  • D-IP: Dependency Inversion Principle: Permite conseguir un desacoplamiento de las clases en el código, de tal manera que si una clase emplea otras clases, la inicialización de los objetos venga dada desde fuera. Además, si se puede sustituir las clases por interfaces, se puede extender a otro objeto que implemente la interfaz, sin variar el parámetro.

Estos cinco patrones componen SOLID, pero hay más, está la ley de Demeter, los los principios DRY… varias buenas prácticas para desarrollar buen software que van bastante más allá del tema de este artículo. Otro detalle importante es que son patrones, no metodologías, se pueden seguir o no, aunque las razones para seguirlos son bastante interesantes.

Inversión de dependencias e inyección de dependencias.

La inversión de dependencias, como se ha dicho en el apartado anterior, lleva implícita la inyección de dependencias. Para explicar este concepto, se puede mostrar el siguiente ejemplo, partiendo de este código inicial.

class MiClase
{
    public MiClase()
    {
        ClaseA a = new ClaseA();
        ClaseB b = new ClaseB();

        a.UnMetodo();
        b.OtroMetodo();
    }
}

Este código tiene un problema de acoplamiento, ya que la clase depende de otras dos clases. Si cambiase el comportamiento de las clases ClaseA o ClaseB, habría que cambiar el código de esta clase, no digamos si los constructores tienen parámetros.

Una primera mejora consistiría en pasar los objetos por argumento, de tal manera que sean definidos fuera de la clase, eliminando la dependencia en esta fase, e inyectándola mediante el constructor, por lo tanto, si el código de estos cambia, no será necesario editar esta clase, pero, nos aseguraremos que el código sigue siendo válido?:

class MiClase
{
    public MiClase(ClaseA a, ClaseB b)
    {
        a.UnMetodo();
        b.OtroMetodo();
    }
}
Una segunda mejora consistiría en sustituir los objetos por interfaces, de tal manera que lo que se pase al constructor de la función no sea una clase, sino un contrato, una implementación que tendrá que cumplir con la interfaz acordada, aunque el contenido de la clase cambie, o sea otra clase completamente diferente que implemente la interfaz:
class MiClase
{
    public MiClase(IClaseA a, IClaseB b)
    {
        a.UnMetodo();
        b.OtroMetodo();
    }
}
De esta manera si se crea una clase OtherClass que implemente IClaseA, se puede pasar por el constructor y el funcionamiento de MiClase no variará, con lo cual no será necesario editar el código. La consecuencia es que esta clase está más desacoplada, lo que permite, por ejemplo, poder pasar objetos de prueba para test unitarios, reducir los fallos y, en definitiva, tener un código más sólido.

Unity, un contenedor IoC

Un detalle del apartado anterior, es que el código no es del todo desacoplado, es decir, estará acoplado a la llamada al constructor o a la función, para terminar de resolver este “pequeño problema” surge Unity, que se define como un contenedor de inyección de dependencias.

El funcionamiento del contenedor permite primeramente registrar los tipos que se usarán, es decir, los objetos se devolverán al solicitar las diferentes interfaces. Una vez se han registrado, para crear los objetos solamente será necesario llamar al contenedor, y él se encargará de resolver la ruta pedida, es decir, emparejar la interfaz que se solicita con el objeto definido en la fase de registro.

Una ventaja de este contenedor es que la configuración se puede realizar vía XML, evitando el hecho de recompilar.

Resumen

Programar no es solamente escribir código, existen estilos y patrones que si los seguimos 1. No hacen daño y 2. Permiten obtener un código legible y mejorar su mantenibilidad. Espero que la lectura te haya resultado interesante, y si quieres más información, te remito a los libros y las charlas de Hadi.

Más información: