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!

¡Microsoft MVP 2012 – APS.NET/IIS!

MVP 2012

Hace uno pocos días recibí una noticia que me puso muy feliz y que quería compartirla con todos ustedes: he sido reconocido como Microsoft MVP (Most Value Professional) en el área de ASP.NET/IIS.

Poder pertenecer al mismo grupo del cual forman parte muchos de los profesionales que respeto y admiro, es para mí un gran placer y orgullo. Además quiero agradecer a todos mis compañeros, colegas y familiares que hicieron posible esta distinción.

Para los que no sepan, el reconocimiento como Profesional Más Valioso (MVP), es la forma en la que Microsoft agradece las contribuciones excepcionales realizadas por expertos independientes en la tecnología o los productos de Microsoft al compartir su pasión por la tecnología, su experiencia y su conocimiento con las comunidades técnicas. Pueden encontrar más info acá.

¡Gracias a todos!

Implementando características de OData con ASP.NET WebAPI

En esta nueva entrega sobre ASP.NET Web API vamos a hablar sobre las características que este soporta del protocolo Open Data Protocol (de ahora en mas OData).

Open Data Protocol

Open Data Protocol

Antes de comenzar, vamos a realizar una pequeñísima introducción a Open Data Protocol.

OData es un protocolo abierto – open protocol – creado por Microsoft para exponer datos como servicio. Este se basa en estándares conocidos de Internet como HTTP, Atom (AtomPub) y JSON. Como todo protocolo de servicios, uno de los fines principales es poder independizar los datos de la aplicación o sitio web que los utiliza. Los clientes que consumen servicios a través el protocolo OData pueden hacerlo bajo formatos como Atom, JSON o XML plano, pero además incluyendo características como paginación, ordenación y filtrosquerys -.

Otra característica interesante de OData es que nos permite exponer y acceder a información de una gran variedad de fuentes, incluyendo, bases de datos relacionales, sistemas de archivos, sistemas de gestión de contenidos y sitios web tradicionales.

Escenarios de despliegue de OData

Escenarios de despliegue de OData

Ahora bien, de todas las características que ofrece OData, la que nos interesa en este momento es la utilización de convenciones URI que nos permitirán, entre otras cosas, realizar operaciones como navegación, filtrado, orden y paginación de datos en la solicitud de un recurso.

URI Components

URI Components

La utilización de estas convenciones nos permiten, desde la misma URI del recurso, especificar query options que serán aplicadas al momento de obtener un recurso. Podemos ver en el gráfico anterior – URI Components – que las opciones de consultas se especifican al final de la URI.

Ejemplos de query options son:

  • $filter : permite aplicar filtros sobre el resultado.
  • $orderby : permite ordenar por alguna condición el resultado
  • $top : permite recuperar un cierto número de resultados.
  • $skip : permite saltear un cierto número de resultados.

Vayamos a un ejemplo, si quisiera obtener la lista de clientes ordenadas por nombre, debería invocar al servicio utilizando la siguiente URI:

http://localhost:[port]/api/clientes?$orderby=Nombre

Ahora bien, ASP.NET Web API trae soporte para un subconjunto de características del protocolo OData. Una de ellas es que podemos trabajar con las convenciones URI que trabajaran en la interacción con los controladores de nuestra API.

Para trabajar con ellas simplemente debemos modificar el tipo de datos de la respuesta de nuestro método. Recordaran que en post anteriores el método retornaba un objeto IEnumerable:

Método de acción Get() retornando un IEnumerable

Método de acción Get() retornando un IEnumerable

En este caso debemos vamos a modificar la firma y el cuerpo del método para que retorne un objeto IQueryable:

Método de acción Get() retornando un IQueryable

Método de acción Get() retornando un IQueryable

Tal como menciona MSDN, el motivo de esta cambio es que la interfaz IQueryable hereda la interfaz IEnumerable, por lo que si representa una consulta, se pueden enumerar los resultados de esa consulta (la enumeración provoca la ejecución del árbol de expresión asociado a un objeto IQueryable). Es tarea del framework armar la consultas correctamente a partir de las query options enviadas en la URI.

Realizado el cambio, vamos a consumir el servicio como lo veníamos haciendo normalmente utilizando la siguiente URI:

http://localhost:[port]/api/clientes
Datos obtenidos del servicio

Datos obtenidos del servicio

Podemos observar que los resultados vienen en el mismo orden que los habíamos agregamos en el array (es decir, sin estar ordenados por alguna condición). Ahora solicitemos el mismo recurso, especificando que vengan ordenados por el atributo nombre:

http://localhost:[port]/api/clientes?$orderby=Nombre
Datos obtenidos del servicio utilizando las convenciones de URL

Datos obtenidos del servicio utilizando las convenciones de URL

Como vemos, utilizando las convenciones URI, es muy simple establecer condiciones-acciones-funciones en la obtención de los recursos! 🙂

Pero eso no es todo, también podríamos trabajar con paginación y filtros. Simplemente debemos agregar en la URI la query correspondiente de acuerdo a nuestras necesidades, algunos ejemplos:

http://localhost:[port]/api/clientes?$filter=Nombre eq ‘Jous’
  • Filtrar utilizando operadores lógicos:
http://localhost:[port]/api/clientes?$filter=Nombre eq ‘Sebis’ or Nombre eq ‘Jous’
http://localhost:[port]/api/clientes?$top=3&$skip=0

También tenemos disponibles un gran conjunto de opciones de query dentro de las cuales podemos encontrar: Select, Top, OrderBy, Expand, Format, DateTime Functions, Math Functions, Type Functions y muchísimas otras más.

Para finalizar, quería comentarles que OData dispone de un conjunto de API’s de creación y consumo de Servicios OData para trabajar desde el lado del cliente con dispositivos mobiles (WP7, Android, iOS), app webs (Silverligth, ASP.NET, HTML 5 + Javascript, Java, PHP, Ruby) y web CMS (Joomla, Drupal). Y también desde el lado del servidor con custom servers (.NET Server, Java, PHP, Node.JS),  databases (SQL Server, MySql, Azure Data) y cloud app (App Engine, Azure).

Espero que les sea de utilidad.

Nos vemos pronto!