c# Object oriented design – Dependency Inversion ¿Cómo aplicas este principio?

Si alguna vez te has preguntado ¿Qué es el principio Dependency Inversion? y ¿Cómo lo puedo implementar? te recomiendo que leas este post.
Principio Dependency Inversion
Principio Dependency Inversion


Artículos relacionados:

El término Dependency Inversion fue introducido por Robert C. Martin en su artículo The Principles of Object Oriented Design y luego lo popularizó en su libro Agile Principles, Patterns, and Practices in C#. Este es el último de los cinco principios SOLID y creo que uno de los más difíciles de aplicar y entender.

Iniciemos con esta definición obtenida del libro Agile Principles, Patterns, and Practices in C#:

A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.

Además, nos dice lo siguiente en su artículo The Principles of Object Oriented Design:

Depend on abstractions, not on concretions.

El principio Dependency Inversion señala que los módulos de alto nivel no deben depender de los módulos de bajo nivel, ambos deben depender de abstracciones (Interfaces, Clases abstractas), no de clases concretas. Además, cambia la forma en que estamos acostumbrados a diseñar, ya que ahora el diseño se debe realizar desde el centro hacia fuera. Esto quiere decir que primero debemos diseñar el core del sistema para detectar cuáles son las dependencias que se necesitan. Una vez definidas estas dependencias, se pasa a crear las implementaciones que van a ser usadas por el core.

Tomacorriente
Tomacorriente

Un tomacorriente permite que cualquier electrodoméstico, que use la misma interfaz estándar, se conecte a él. Se imaginan si en lugar de usar un tomacorriente hubiéramos unido directamente los cables de un televisor a los cables de la energía eléctrica, esto hubiera evitado que otros aparatos se puedan conectar. Lo mismo ocurre con el desarrollo de software. En lugar de trabajar con clases unidas (acopladas) entre si, se debe trabajar con interfaces como lo define el principio Dependency Inversion.

¿Sobre qué artefactos aplica este principio?
  • Clases
  • Paquetes
  • Módulos
¿Qué beneficios trae el trabajar con este principio?
  • Bajo acoplamiento.
  • Testeabilidad.
  • Flexibilidad.
¿Cuándo se recomienda aplicar este principio?
  • Cuando se necesitan desacoplar piezas de software que pueden cambiar en el futuro.
  • Cuando el nivel de acoplamiento en el código es alto.
¿Cuándo no debemos aplicar este principio?
  • Cuando se desarrolla un módulo CRUD opequeño .
Argumentos en contra de este principio
  • Requiere mayor experiencia
  • Requiere más esfuerzo el diseñar los artefactos
  • Demasiadas interfaces
  • Complicado entender el panorama general del diseño

Veamos los siguiente casos:

A. Primera sentencia: High-level modules should not depend on low-level modules.
Cómo no aplicamos este principio

Cuando las clases se encuentran acopladas, el cambio de una afecta a la otra. En este caso la clase ServicioAutenticacion está unida a la clase RepositorioCredencial. Recuerden que la primera parte de este principio señala que los módulos de alto nivel no dependen de los módulos de bajo nivel.

Clase unida a otra
Clases acopladas
Cómo aplicamos este principio

Para corregir esto la clase ServicioAutenticacion solo debe depender de una interfaz y la clase RepositorioCredencial debe implementarla.

Clase usa una abstracción
Clases desacopladas
B. Segunda sentencia: Abstractions should not depend on details. Details should depend on abstractions
Cómo no aplicamos este principio

Esto ocurre a menudo cuando usamos el estilo de arquitectura en Capas. Primero diseñamos la capa de datos, dentro de esta definimos las interfaces de los repositorios y las implementaciones. Luego pasamos a diseñar la capa de negocio, la cual depende de las interfaces de los repositorios para saber que operaciones puede usar. El problema radica en el lugar donde se ubican las interfaces del repositorio, ya que no es el adecuado. Al trabajar de esta forma la abstracción (IRepositorioCredencial) depende del detalle (RepositorioCredencial) por encontrase en el mismo paquete o proyecto.

Abstracción depende del detalle
Abstracción depende del detalle, se encuentran en el mismo paquete
Cómo aplicamos este principio

Para corregir esto debemos mover las interfaces de los repositorios al paquete de lógica de negocio. Con esto logramos que el detalle (RepositorioCredencial) dependa de las abstracciones (IRepositorioCredencial).

Abstracción no depende del detalle
Abstracción no depende del detalle, se encuentra en otro paquete

Veamos cómo invertir la dependencia en capas

Arquitectura tradicional en Capas

En este estilo las capas superiores dependen de las inferiores y se encuentran altamente acopladas entre sí, un cambio en base de datos obligara a que también se realicen cambios en las otras capas.

Arquitectura tradicional en capas
Arquitectura tradicional en capas
Arquitectura desacoplada en Capas

Cada capa no debe saber nada acerca acerca de como trabajan las otras capas. Al trabajar con interfaces bajamos el acoplamiento entre ellas, ahora una capa de nivel superior solo depende de las interfaces de la siguiente capa, al haber un cambio en una capa de nivel inferior no debe obligar a que las capas superiores sufran cambios. Pero igual se mantienen las dependencias con los elementos de infraestructura.

Arquitectura tradicional en capas desacoplada
Arquitectura tradicional en capas desacoplada
Una mejor alternativa: Arquitectura invertida

Con este tipo de diseño cumplimos con lo que menciona el principio Dependency Inversion. Primero desarrollamos el core (módulos de alto nivel) para luego pasar a implementar las abstracciones (módulos de bajo nivel). Este estilo de arquitectura es conocido como Onion Architecture o Clean Architecture.

Arquitectura invertida
Arquitectura invertida

Con este post terminamos la serie de artículos acerca de los cinco principios SOLID, no fue fácil escribirlos pero se consiguió el objetivo :).

Conclusión

El principio Dependency Inversion indica que los módulos de alto nivel no deben depender de los módulos de bajo nivel ambos deben depender de abstracciones (Interfaces, Clases abstractas), no de clases concretas. Este principio se aplica a clases, paquetes y módulos. Como principales beneficios tenemos que nos permite bajar el acoplamiento y volver el código testeable.  No se debe aplicar el principio Dependency Inversion si solo se va a trabajar con un modulo CRUD.  Como argumentos en contra tiene que requiere más experiencia y esfuerzo para diseñar los artefactos.

Referencias:
Metal Tip:

Este artículo lo escribí escuchando la canción Monolith of Doubt de la banda After Forever de Holanda, les comparto el enlace.

Happy coding and Stay Heavy lml

Anuncios

10 comentarios en “c# Object oriented design – Dependency Inversion ¿Cómo aplicas este principio?

  1. Excelente aporte!,
    Aun que no se si estoy en lo correcto pero lo relacione mucho con
    el MVC(Model View Controller). Donde mi modelo de nogocio es populado o accesado en la view(presentacion) atravez de un controller!.

    Le gusta a 1 persona

    1. Hola Michael, el tema de las interfaces y los paquetes es que deben estar en el core. Al estar, por ejemplo, la interfaz IRepositoriousuario en el paquete de datos obliga a que el paquete de negocio dependa de el, en cambio si la interfaz se encuentra en el paquete de negocio se invierte la dependencia. Recuerda los módulos de alto nivel(lógica) no dependen de los módulos de bajo nivel(datos). Saludos 😉

      Le gusta a 1 persona

  2. Muy buen post, muy completo
    Me quedo una duda de lo que comentas: ” Ademas cambia la forma en que estamos acostumbrados a diseñar, ya que ahora el diseño se debe realizar desde el centro hacia fuera. Esto quiere decir que primero debemos diseñar el core del sistema para detectar cuáles son las dependencias que se necesitan. Una vez definidas estas dependencias, se pasa a crear las implementaciones que van a ser usadas por el core”.
    Te referias a que el core seria la capa de negocio o dominio y a partir de ahi crear interfaces que son las dependencias abstractas con otras capas , que finalmente implementamos en el core?
    Este principio no lo vi a nivel de capas , por eso me cuesta comprender ,siempre lo vi partiendo de la interfaces y a partir de esta implementar las modulos de bajo nivel y alto nivel.

    Gracias.

    Le gusta a 1 persona

    1. Hola Sebastian, se puede decir que el core seria la capa de Negocio por darle una similitud. La idea es que este core no tenga dependencias externas. En la arquitectura tradicional de 3 capas, la capa de Negocio depende de la capa de datos, bajo este enfoque la capa de datos infraestructura) dependería de negocio, ya que las implementaciones que datos necesita hacer se encuentran definidas en Lógica (Core). Saludos

      Me gusta

      1. fijate que tengo un problemita, es relacionado a lo que dijo Sebastian, estoy tratando de practicar lo dices en el post, pero en la capa de negocios que hace la funcion de core, ahi tengo llamar a metodos que traen datos de la capa de datos, y tu dices que la capa de negocios no dependeria de la de datos, ahi estoy un poco confundido.
        Gracias

        Me gusta

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