martes, 5 de febrero de 2013

Principios de Diseño

Hace poco terminé de leer el excelente y altamente recomendable libro de Cecilio Álvarez Caules sobre Arquitectura Java. El libro es muy interesante porque explica cómo, a partir de una sucesión de refactorizaciones guiadas por el sentido común, una aplicación bien simple de gestión de libros de una biblioteca evoluciona desde una arquitectura de una única capa, con JSP y JDBC directo a una base MySQL, hasta una arquitectura clásica Java EE de tres capas, implementada con JSF, Spring y JPA.

En realidad ese "sentido común" son más bien los Principios de Ingeniería, o como a mí me gusta llamarlos: Principios de Diseño, y esto es de lo que quiero hablar en este post.

Los principios a los que el libro hace mención son los cinco de SOLID (de los cuales ya hablé una vez) y tres más que son ampliamente conocidos. Me voy a tomar el atrevimiento de copiar las definiciones del libro (agregando algunas anotaciones mías) así los entusiasmo para leerlo:
  • DRY (Don't Repeat YourSelf): implica que cualquier funcionalidad existente en un programa debe existir de forma única en él, o lo que es lo mismo, no debemos tener bloques de código repetidos. (Nota mental: cortarme la mano antes de abusar del copy & paste.)
  • SRP (Simple Responsibility Principle): toda clase o componente debe tener una única responsabilidad y todas sus funciones deben orientarse hacia ésta. Otra forma de enfocarlo es: una clase, al tener una única responsabilidad, sólo debe ser alterada a través de un cambio en dicha responsabilidad.
  • OCP (Open Closed Principle): todo código desarrollado para una aplicación debe estar cerrado a las modificaciones y abierto a la extensibilidad. Expresado de otra manera: debemos poder añadir nueva funcionalidad a la aplicación sin tener que alterar el código ya construido. (En la Orientación a Objetos el mecanismo por excelencia para lograr esto es la herencia, y esto nos lleva al siguiente principio que es...)
  • LSP (Liskov Substitution Principle): se menciona en el libro pero no se explica. Este principio está muy relacionado con OCP, y también con la buena práctica de siempre trabajar contra interfaces en lugar de implementaciones (interfaces o la clase más alta de una jerarquía). Una porción de código, digamos un método, debería conocer la clase padre, y funcionar de modo correcto si como input recibe una clase hija. Así, ese método estará cerrado ante modificaciones pero abierto a la extensibilidad, ya que podremos cambiar comportamiento simplemente creando una nueva clase hija y sobreescribiendo algún comportamiento de la misma.
  • IOC (Inversion of Control): consiste en que el control de la construcción de los objetos no recae directamente en el desarrollador a través del uso del operador new, sino que es otra clase o conjunto de clases (habitualmente llamado Contenedor) las que se encargan de construir los objetos que necesitamos.
  • DIP (Dependency Injection Principle): las dependencias que una clase tiene no deben ser asignadas por ella misma sino por un agente externo (a menudo llamado Contenedor). Podemos lograr Inversión de Control con un Factory, por ejemplo, pero si queremos Inyección de Dependencia tendremos que usar Reflection o algún framework como Spring o CDI.
  • COC (Convention Over Configuration): define que, antes de abordar el desarrollo, un programador puede declarar una serie de convenciones que le permiten asumir una configuración por defecto del sistema.
  • ISP (Interface Segregation Principle): una clase cliente A que tiene una dependencia con la clase B no debe verse forzada a depender de métodos de la clase B que no vaya a usar jamás.
Por último, Caules se hace una pregunta muy importante: ¿Qué es lo más importante a la hora de diseñar una aplicación? ¿Los Frameworks que se van a utilizar, los Patrones de Diseño, o los Principios de Ingeniería (SOLID + DRY + IOC + COC)?

La respuesta que da se puede vislumbrar en el siguiente diagrama:


Los frameworks están construidos apoyándose en distintos Patrones de Diseño y un conocimiento sólido de éstos nos permitirá entender de una forma más natural el funcionamiento de los frameworks. Podemos dar como ejemplos el patrón Data Mapper de Martin Fowler, en el cual se basan todos los ORMs como JPA, o Repository usado por Spring para anotar los DAOs, o Front Controller usado por muchos frameworks MVC como JSF o Struts, y así podría seguir todo el día.

Si seguimos profundizando en este análisis, pronto nos daremos cuenta de que los Patrones de Diseño no salen de la mente retorcida de diseñadores y arquitectos porque sí, sino que nacen para satisfacer Principios básicos de Ingeniería. Así, el patrón MVC aparece una vez que se dividen las responsabilidades de Modelo, Vista y Controlador usando el principio SPR, el patrón Front Controller surge cuando se quiere obtener la propiedad OCP en una capa de presentación, o el patrón Template queriendo aplicar DRY en una jerarquía de clases concreta.

Lo más importante para los arquitectos, concluye Caules, es conocer estos principios de diseño de software ya que nos facilitará sobremanera el entender por qué un código se ha de construir de una manera u otra.