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!

Anuncios

6 comentarios en “DIP – Principio de Inversión de Dependencias

  1. Pingback: MVP Factor

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