Refactoring aplicando patrones

Me topé hace poco con un problema que podría decirse que es bastante común.
El problema puede que sea resultado de un mal análisis o simplemente de un momento de cansancio. Del mismo modo, puede ser que la solución final no sea la mejor.
Pero sinceramente me pareció un caso bastante sencillo para mostrar como un modelo puede evolucionar utilizando patrones.
No podría decir a ciencia cierta si utilicé sólo un patrón, porque como hemos visto hasta el momento, los patrones no son soluciones mágicas que sirven para cualquier modelo o que se aplica de una única manera. Sino que más bien son plantillas que hasta pueden mezclarse entre sí para poder llegar a un resultado que nos resulte útil para que sea escalable.

Problema

El dominio de problema que se debía solucionar, era la generación o armado de sentencias para tablas de estructuras idénticas pero que se llamaban distinto (Organizacion y Puesto) y estaban en diferentes esquemas de Sql Server (Parametros y Registros).
Otro problema que se sumaba, era que las tablas de Puesto poseen una estructura algo diferente a la de Organización, por lo que la sentencia que debía devolver, también debía ser diferente.

Primer diseño 

Este se basó en crear una clase abstracta que contenía la lógica básica con métodos que devolvían cada una de las sentencias necesarias. Luego, dos clases derivadas de ésta (también abstractas) para diferenciar los esquemas. Para luego, finalmente, realizar cuatro nuevas clases (dos para cada esquema) para implementar el nombre de la tabla.
Además, la clase para las tablas de Puesto sobrescribían un método para poder devolver la sentencia necesaria, que era diferente a la de la clase abstracta base.
Este diseño resolvía el problema, pero ocasionaba algo que no quedaba bien: había un método repetido (GenerarInsertTablaEspecifica) en dos clases y exactamente iguales en contenido.


Segundo diseño

La mejora del diseño decantaba en lo evidente: había que remover la duplicidad de código. Lo primero que se me ocurrió fue sacar ese método a un colaborador. El método estaría escrito una única vez y podía ser reutilizado por cualquiera de las clases que la necesitaban. Traté de implementar el patrón Strategy. El colaborador debía ser intercambiable. Para esto creé dos clases: ArmadorSentenciasOrganizacion, conteniendo la lógica de la clase base y ArmadorSentenciasPuesto, conteniendo la lógica diferenciada. Ambas implementando una interfaz: IArmadorSentenciasTablaEspecifica
Modifiqué el constructor de clase abstracta base para inyectar un colaborador que satisfaga a ésta interfaz.
Ahora sí, no más código repetido... o si?
Pues si. Aunque no se note, sigue habiendo código repetido. Por que como este diseño está siendo implementado en C#, estoy obligado de replicar el constructor en todas las clases derivadas de la clase base simplemente para que cada constructor invoque al constructor de ArmadorSentenciasParametrosYRegistros.




Tercer diseño

No quería darme por vencido. Tenía que haber una forma de satisfacer al diseño y a mi empecinamiento de no dejar código repetido. De pronto recordé otro patrón: Private class data. Aunque no calzaba exactamente. Su concepto de separar los datos de los métodos que los usan me sirvió.
Creé una nueva clase: Tabla, que implementa una nueva interfazIDatosArmadoSentencias. A través de este nuevo objeto se proporciona a la clase ArmadorSentenciasParametrosYRegistros la información que necesita para completar las sentencias que se necesitan: El esquema y el nombre de la tabla.
El nuevo objeto sólo oficia de contenedor de datos. No agrega funcionalidad a la clase que arma las sentencias.
Al ser inyectado a través del constructor, la clase ArmadorSentenciasParametrosYRegistros ya no necesitaba tener otras clases derivadas, por lo que deja de ser abstracta. Queda genérica para cualquier esquema y tabla que se necesite, incluso más allá de las mencionadas al principio de este ejemplo (aunque para ello se deberá cambiar el nombre de la clase para ser más explícito).


Conclusión

El diseño, puede seguir siendo mejorado (Cómo mínimo, los nombres de las clases e interfaces). Pero no es el fin buscado de este post continuar con las mejoras.
Simplemente buscaba mostrar como un diseño inicial, que resuelve un problema concreto, puede seguir evolucionando. No sólo resolver el dominio de problema debe ser la meta de un diseño, sino también la posibilidad de ser escalable, reutilizable y perdurable en el tiempo.
Recordemos que el refactoring debe ser parte del ciclo de programación.

0 comentarios:

Publicar un comentario

Muchas gracias por leer el post y comentarlo.

 
Copyright 2009 Programación SOLIDa
BloggerTheme by BloggerThemes | Design by 9thsphere