Patrones de diseño

Tengo una pequeña biblioteca digital en la cual voy guardando libros que me gustaría leer, me encanta releer o aún no consigo su copia en físico. Esa pequeña biblioteca es de temas variados, literatura, poesía, psicología, desarrollo de software, entre otros.
Platicando con un amigo, me hizo recordar esta curiosa antología y hallé el libro Patrones de Diseño, de nuestros 4 jinetes del apocalipsis de la programación orientada a objetos (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides). ¿Coincidencia? No lo creo. Así que, decidí darle una leída al libro.

Patrones de diseño: un paradigma entero contenido en un libro.

Una leída rápida pero completa. Entre siestas y capítulos de la serie en turno lo terminé en un par de días.
Este post se tratará de una discusión sobre los interesantes conceptos ofrecidos en el libro Patrones de Diseño. Además, haré lo posible por enumerar algunos proyectos en los cuales considero he aplicado ciertos de estos patrones.

Debo de dejar claro que, luego de leer a Gamma y compañía, me doy cuenta de que en realidad, mi capacidad de diseño orientado a objetos tiene aún mucho que desear, pero haré lo posible en explicar lo que he aprendido hasta ahora.

Reflexiones introductorias

Primeramente, me he hallado con la conclusión imperante:

Reusar código es bueno.

Reusar la mayor cantidad de código posible, sabiendo cuando es posible reutilizar, es lo que caracteriza a un desarrollador experimentado. Dicen por ahí que más sabe el diablo por viejo que por diablo.

Reusar, clonar, nos evita tiempo y esfuerzo al crear.

Además, es importante que nuestros sistemas estén formados por objetos que tengan interacciones débiles entre sí, para que los objetos sean cajas negras para otros objetos. Solo envías datos y recibes respuestas. No deberías preocuparte por el comportamiento interno de otro objeto. Si existen interacciones débiles, es fácil reemplazar un objeto que no se halle atado a otros.

El objetivo entonces es identificar las características comunes en problemas, para modelar una misma solución para problemas distintas. Buscamos características comunes . . . ¿Qué no es eso la definición de un patrón?

Luego de un análisis exhaustivo de problemas comunes en el diseño de software, llegaron a la conclusión de generar soluciones de diseño que funcionan de forma general para muchos contextos.
Es decir, los patrones de diseño son soluciones generales a un conjunto de problemas comunes en el diseño (de software).
Se recomienda ampliamente comprender y aprender a usar estos patrones de diseño, a fin de cuentas, son soluciones validadas para una gran gama de problemas. Suena a mucho ahorro de tiempo.

Los patrones de diseño busca desacoplar los distintos subsistemas de software, el procesamiento de datos se realiza en objetos y subsistemas distintos a la representación de estos, por ejemplo.
Desacoplar, reusar, anidar. Es el fin último de un buen diseño en software. Para hacer un buen diseño de nuestro sistema, es necesario comprender que nuestros objetos realizarán operaciones y tendrán comportamientos. La única forma que nuestros objetos realizarán dichas operaciones es al enviarle un mensaje o petición al objeto. Estas operaciones son la única forma de cambiar la información interna del objeto y por ende, cambiar su estado actual.

Diseña interfaces, no clases:

Ha sido una frase que aparece más de una vez en la lectura. Tiene sentido, porque a fin de cuentas, el objetivo buscado es generar un sistema con comportamiento consistente. Lo importante aquí es diseñar un sistema en el cual se tengan bien conocidas las operaciones realizables por cada una de sus clases. Por ahí dicen que somos definidos por nuestras acciones, con el software no es la excepción.
Si ladra y mueve la cola, ha de ser un perro.
La implementación se vuelve secundaria ante un buen diseño. El diseño es la abstracción del sistema, la implementación su especificación. Los objetos pueden trabajar perfectamente como cajas negras, solo conociendo sus operaciones.

El diseño se enfoca en los fines, la implementación en los medios.

Definición de un patrón de diseño

Un patrón de diseño es una solución que puede ser aplicable a una gran variedad de problemas similares. Estos se han expresado a través de una serie de elementos que lo describen:

Nombre y clasificación: El nombre debería reflejar en una o dos palabras la esencia del patrón.
Propósito: Un par de frases breves que describan que es lo que hace el patrón, que problemas intenta resolver.
Aplicabilidad: Se listan situaciones comunes donde se puede aplicar el patrón. Esto ayuda a identificar pobres diseños, para modificarlos.
Estructura: Una representación gráfica de las clases.
Participantes: Las clases y objetos participando en el patrón de diseño y sus responsabilidades.
Colaboraciones: Como los participantes colaboran en cumplir sus responsabilidades
Consecuencias: Cuáles son los resultados de usar el patrón.
Implementación: Pistas, técnicas y trampas comunes debes conocer antes de implementar el patrón.
Ejemplo: Fragmentos de código que muestran como debería implementarse el patrón.
Usos conocidos: Ejemplos de sistemas reales en los cuales debería aplicarse el patrón.
Patrones relacionados: Sin sal no hay pimienta, hay patrones fuertemente relacionados con este patrón, se enumeran y se enlistan las diferencias y similitudes entre ellos. En algunas ocasiones dichos patrones son complementarios.

Catalogo de patrones

En este post hablaré de algunos de los patrones que más me gustan/interesan/conozco/ o he implementado. No hablo de todos, esta historia se está volviendo muy larga.
Hay tres tipos de patrones: Creacionales, estructurales y de comportamiento.

Patrones de Diseño Creacionales

Es importante separar las operaciones de creación, composición y comportamiento de los objetos. Los patrones de diseño creacionales se basan en la instanciación de los objetos ( o la delegación de operaciones de creación a otras clases).

Fabrica abstracta: patrón de diseño que te permite producir un conjunto de objetos relacionados sin especificar sus clases concretas. Este patrón de diseño especifica solamente tanto las operaciones como características que conllevará el objeto. Busca dejar de forma explicita las interfaces de creación de los objetos. Este patrón de diseño no se encarga de la implementación. En mi opinión considero que este patrón es útil, porque deja en claro las características generales de los objetos a instanciar.

Constructor: El constructor permite separar el proceso de instanciación de la representación del objeto. De esta forma podemos crear objetos con distintas representaciones utilizando el mismo constructor. El constructor construye de forma secuencial, paso por paso. Es uno de los patrones de diseño que más he utilizado en mi corta vida como desarrollador. De cierta forma, estamos acostumbrados a esa construcción secuencial. Construimos las paredes, luego los techos. No en otro orden. No somos maestros jedi para construir por separado techos y paredes, para luego ponerlas en su lugar simultaneamente ( o si lo somos?).

Prototipo: Especifica el tipo de objetos a crear utilizando una instancia a partir de un prototipo. Es muy común crear un nuevo documento a partir de alguna plantilla específica. La clave está en clonar un objeto modelo para crear nuestra nueva instancia. Gracias al prototipado nos ahorramos la creacíon de código extra. A fin de cuentas, tan solo copiamos y pegamos. Es útil para instanciar clases dinámicas, es decir, especificadas en tiempo de ejecución, por ejemplo.

Las plantillas nos son útiles para la creación de nuevos objetos.

Singleton: Asegura que la clase solamente tiene una instancia y proporciona un acceso global a ella. Este patrón de diseño lo he utilizado cuando se refiere a generar una única aplicación que se conecte con un servicio externo ( como podría ser una API), se utiliza un singleton para representar esta única aplicación. Imaginan que tu aplicación no sea un singleton y por error intentes instanciarla una y otra vez? Sería catastrófico.

Estructurales

Los patrones de diseño estructurales definen como interactuarán los objetos para generar estructuras más grandes. Así como en el cuerpo humano las células componen tejidos, los tejidos órganos y los órganos sistemas. Los patrones de diseño estructurales definen como un conjunto de objetos realizan una nueva funcionalidad.

Adaptador: Cuando me hallaba en mi internship el verano pasado, mi primer tarea consistió en familiarizarme con cierta API, para un servicio de chat. Dicha API contenía una documentación algo oscura y las interfaces que entregaba eran bastante generales y complejas ( los chats que ofrecían eran del tipo abierto, como un canal de conversación en Twitch o un chat en vivo de Youtube. Nosotros deseábamos acoplarlo para crear chats del estilo de Messenger, solo dos usuarios y con ciertas restricciones de acceso). Luego de un par de días, en el que me familiaricé, me di cuenta del suplicio que sería implementar dichas interfaces, una y otra vez donde se requirieran. Poseía muchos parámetros cada firma y teníamos que ajustarlos de forma específica a nuestras necesidades estáticas. Mi primer idea que me vino a la mente fue proteger a mi equipo de esos dolores de cabeza, al crear mi propia interfaz que adapte la de la API. Mi equipo utilizaba mis procedimientos y yo me peleaba con los parámetros que necesitaba la API. Sin saberlo, acababa de realizar un Adaptador.

Adaptador: transforma una interfaz en otra utilizable por el sistema.

Decorador: añade responsabilidades adicionales a un objeto de forma dinámica. Este patrón no pretende extender la clase del objeto, solo pretende agregarle nuevas responsabilidades o características. Estos adornos pueden ser independientes del objeto, por lo que pueden utilizarse en distintas clases. Es fácil entenderlo como una piel del objeto. No es el objeto ni cambia su comportamiento, pero puede cambiar sus propiedades.

Facade: Unifica las distintas interfaces de subsistemas. De esta forma, la interfaz única que observa el cliente representa el comportamiento del sistema como conjunto. Esto simplifica el uso del sistema, al darle un sentido de pertenencia a los distintos subsistemas en el sistema conjunto. Además puede ofrecer protección del cliente a las subclases internas del sistema. El facade es muy útil para darle una fachada simplificada al cliente, cuando hace uso del sistema.

Proxy: ofrece un contenedor para controlar el acceso a cierto objeto. En algunas ocasiones el objeto en cuestión es costoso de instanciar. Aplicamos una instanciación floja ( lazy instantiation), en otras palabras, lo instanciamos on demand mientras no sea instanciado, el proxy nos indicará que el objeto debería estar ahí y lo instancía cuando es necesario.

Patrones de Diseño de comportamiento

Los patrones de diseño de comportamiento se enfocan en los algoritmos y la asignación entre objetos. Los patrones de comportamiento se enfocan en la comunicación entre objetos, el encapsulamiento de responsabilidades y la delegación de estas. En tiempo de ejecución, los objetos se comunican entre sí, dando como consecuencia un flujo de control que puede volverse complicado fácilmente. Los patrones de diseño aquí se enfocan en que dichas interacciones entre objetos sean lo más débiles posibles, habiendo poco acoplamiento, los objetos trabajan cuasi independientemente, o al menos, en sus propios términos.

Cadena de responsabilidad: Evita el acoplamiento de emisor de una petición con su receptor, al dar la oportunidad a más de un objeto de responder a la solicitud. En la cadena de mando, si un objeto no puede manejar la respuesta a la solicitud, la pasa al objeto que está por encima de él en la cadena. Suena bastante comprensible, no? Este patrón de diseño se utiliza cuando más de un objeto puede manejar la petición, pero no sabemos a priori quien debe manejarlo. Por jerarquía debería asignarse automáticamente al receptor correspondiente.

Memento: ¿Alguna vez, luego de jugar un videojuego por largo tiempo lo has tenido que guardar? El patrón de diseño memento caputura y externaliza los valores de un objeto encapsulado, para luego ser restaurado a dicho estado. Más simple no lo puedo explicar.

Observer: Este patrón de diseño tiene analogía con un vocero, o la prensa. Mantiene una relación 1 a muchos, en la cual notifica a los objetos dependientes sobre el cambio del objeto observado.

Conclusiones
Luego de este ejercicio suicida de leer un libro técnico en menos de una semana ( y vaya que es una biblia el libro Patrones de Diseño) aprendí un par de cosas interesantes:

Primero: Me doy cuenta que sé menos de lo que pensaba referente a programación orientada a objetos. Lo cual, me alegra mucho. Conocer que se ignora, es una gran oportunidad de aprendizaje.

Segundo: los patrones de diseño se hallan implementados en nuestra vida cotidiana. Tengo la teoría que hemos llegado a implementarlos debido al cambio paradígmico que ha sufrido la sociedad moderna. La revolución industrial, la medicina, la arquitectura y el arte moderno fueron esos pioneros ocultos en el paradigma orientado a objetos, en mi opinión. Descubrieron el paradigma a su modo. Es por ello que actualmente, el aprendizaje de dichos patrones de diseño y el entendimiento de este paradigma es relativamente fácil de visualizar, ya que por poco más de un siglo ( al menos ) hemos vivido, sin saberlo, inmersos en él.

Tercero: El libro Patrones de diseño, no es una lectura para un fin de semana en la playa. Es un libro robusto con complejidad técnica y ejemplos no triviales. Es impresionante que The gang of four haya sido capaz de delinear estos patrones de diseño, que sean válidos y consistentes para una gran gama de casos. En lo personal me fija el objetivo de poder crear una obra de este estilo, que agrupe un conjunto de ideas que den a luz a un nuevo paradigma. Pero eso, será harina de otro costal.

Deja un comentario

Diseña un sitio como este con WordPress.com
Comenzar