Conceptos de Diseño Patrones, Tácticas y Frameworks

Publicado en

En la columna dedicada al diseño de la arquitectura (SG #29) se mencionó que el proceso de diseño involucra la toma de decisiones que dan lugar a las estructuras del sistema. Estos incluyen aspectos tales como los patrones de diseño, las tácticas y los frameworks. En esta columna revisaremos en más detalle estos conceptos y discutiremos la relación que existe entre ellos.

Patrones de diseño

El concepto de patrones de diseño se origina en la arquitectura de edificaciones y fue popularizado por el arquitecto Christopher Alexander y sus colegas en el libro “A Pattern Language: Towns, Buildings, Construction” [1]. Alexander presenta una colección de patrones que son soluciones conceptuales a problemas recurrentes de diseño que se aplican a distintas escalas. Los autores establecieron la estructura de un catálogo de patrones de diseño: cada patrón tiene un nombre que lo identifica; una descripción del problema o contexto; una descripción de la solución conceptual; una discusión de las implicaciones del patrón; y su relación con otros patrones.

El conjunto de nombres que identifican a los patrones dan lugar a un lenguaje de diseño. La idea es que con solo utilizar el nombre de un patrón, un diseñador hace referencia a la solución conceptual que representa el patrón de forma rápida, sin tener que describir todos los detalles (esto es, si las demás personas hablan el mismo lenguaje de diseño).

El concepto promovido por Alexander fue retomado en el software y popularizado en el libro “Design Patterns: Elements of Reusable Object Oriented Software” [2]. Debido a que son cuatro autores, a este libro también se le conoce como el “Gang of Four” (GoF). En el GoF se documentan 23 patrones que son soluciones conceptuales a problemas recurrentes de diseño de software. El concepto de patrones de diseño resultó extremadamente popular y después del GoF han aparecido una gran cantidad de libros de patrones de diseño enfocados a temas específicos como pueden ser diseño de arquitecturas, creación de sistemas distribuidos,  sistemas tolerantes a fallas, seguridad, integración de sistemas, etcétera. Existen cientos de patrones de diseño documentados y una dificultad ahora es elegir patrones específicos de entre la gran variedad.

Para ejemplificar el uso de patrones consideremos el caso de un sistema de subastas en línea en donde se tiene una gran cantidad de usuarios que están realizando ofertas de un producto desde su navegador. En todo momento, los usuarios deben poder ver el precio actual del artículo que les interesa y cuando alguno de los usuarios realiza una oferta, todos los demás usuarios deben de poder ver el último precio que se ofrece por el artículo

Este es un problema común y justamente el GoF documenta un patrón llamado “Observador” (Observer) cuya intención es “definir una dependencia entre objetos de uno a muchos de tal forma que cuando cambia el estado del objeto observado, todos sus dependientes sean notificados y actualizados de forma automática”. La intención de este patrón es muy acorde al problema que intentamos resolver. La figura 1 muestra la estructura base de la solución propuesta por este patrón.

 

Figura 1. Estructura del patrón Observador

 

Como se puede apreciar, existen varios observadores que están interesados en conocer cambios en el estado del sujeto. Los observadores se pueden registrar como interesados en recibir notificaciones de cambios del estado del sujeto mediante el método agrega(). Cuando ocurre un cambio en el estado del sujeto, se invoca el método notifica() del mismo. Este método tiene como consecuencia que a cada uno de los observadores registrados se les envíe una notificación de cambio mediante la invocación al método actualiza(). Al recibir la notificación, los observadores solicitan el nuevo estado del sujeto y actualizan su estado local de acuerdo al nuevo estado del sujeto.

En el contexto del sistema de ejemplo, podemos suponer que la subasta es el sujeto, y los participantes son los observadores. Cuando un usuario envía una nueva oferta, cambiará el estado del sujeto y se enviará un aviso a los observadores para que actualicen su interfaz. De esta manera vemos como un patrón de diseño nos puede ayudar a resolver un problema de diseño de forma relativamente simple y sin que tengamos que “reinventar la rueda”.

Tácticas

Las tácticas para el diseño de arquitecturas de software son un concepto que fue popularizado en el libro “Software Architecture in Practice” de Bass y sus colegas del Software Engineering Institute (SEI) [3]. Las tácticas son decisiones de diseño que influyen en el control de la respuesta de un atributo de calidad. A diferencia de los patrones de diseño, las tácticas son soluciones menos detalladas y están enfocadas a atributos de calidad específicos. Las tácticas son presentadas de forma jerárquica como se muestra en la figura 2. A la raíz del árbol se presenta la categoría de atributo de calidad y debajo de esta categoría se muestra una serie de “preocupaciones” (concerns). Finalmente, debajo de las preocupaciones se encuentran las tácticas específicas. La manera de entender esto es la siguiente: si se tiene que satisfacer el atributo de calidad de desempeño, una posible preocupación es influir sobre la demanda de recursos. Para lograrlo una posible opción es incrementar la eficiencia computacional y esto se puede llevar a cabo por ejemplo mediante algoritmos más eficientes.

 


Figura 2: Tácticas de desempeño

 

Las tácticas se aplican en conjunto con los patrones durante el diseño de la arquitectura de un sistema. Para entenderlo, retomemos el ejemplo anterior y consideremos que existe un escenario de atributo de calidad de desempeño como el siguiente:

•    “Un usuario envía una oferta en un momento en que hay otros 500 usuarios conectados, la oferta es procesada exitosamente y transcurren menos de 20 ms entre la recepción de la petición y el envío de las actualizaciones”

Para poder lograr un escenario como este, podemos referirnos al catálogo de tácticas. En este caso la preocupación principal es la administración de recursos, que en este caso son el procesador y los datos que corresponden al estado. Dos tácticas nos pueden apoyar para resolver este problema: la introducción de concurrencia y el mantenimiento de copias múltiples. De esta forma, en vez de procesar cada petición de forma secuencial y esperar a que terminen de ser notificados todos los programas cliente antes de procesar la siguiente oferta, podemos realizar este proceso de forma concurrente. Por otro lado, el mantenimiento de copias múltiples se puede lograr, por ejemplo, mediante la introducción de un cache de tal forma que cada que se tenga que notificar a los programas cliente no sea necesario tener que acceder a la base de datos para recuperar el estado.

De esta forma podemos ver cómo los patrones y las tácticas se combinan para resolver problemas de diseño permitiendo así estructurar el sistema y satisfacer los atributos de calidad.

Frameworks

Si bien los patrones y tácticas son fundamentales en la creación arquitecturas de software, estos conceptos resultan abstractos y en ocasiones no es claro cómo conectarlos con la tecnología que se usa para el desarrollo. Es así que otro elemento clave son los Frameworks. Estos son elementos reutilizables de software que proveen funcionalidades genéricas enfocadas a resolver cuestiones recurrentes. Existen frameworks para resolver distintos aspectos de una aplicación; por ejemplo, Java Server Faces (JSF) es un framework para  crear interfaces de usuario en aplicaciones web. Existen muchos otros frameworks para resolver todo tipo de aspectos, como el manejo de XML, el acceso a bases de datos relacionales, manejo de concurrencia, etcétera.

Los frameworks típicamente se basan en patrones y tácticas. Un ejemplo de ello es el framework Swing para creación de interfaces de usuario en Java, que aplica patrones como Modelo-Vista-Controlador (MVC), Observador y Composite; e incorpora tácticas de modificabilidad y usabilidad tales como la generalización de módulos y la separación de elementos de interfaz de usuario.

 

Conclusión

El diseño de arquitecturas de software se basa en la toma de decisiones que involucran la selección de conceptos con el fin de satisfacer los requerimientos que influyen sobre la arquitectura. Dentro de estos conceptos se encuentran los patrones de diseño, las tácticas y los frameworks. Uno de los retos importantes del diseño es la selección oportuna y adecuada de dichos elementos, en particular porque existen cientos de patrones de diseño y una gran variedad de frameworks. Además de asegurarnos de que los conceptos de diseño seleccionados satisfagan los requerimientos que influyen sobre la arquitectura, debemos tomar en cuenta el impacto negativo que pudieran tener en la satisfacción de los atributos de calidad. Un ejemplo de ello es la introducción de un framework de persistencia que facilita el mapeo orientado objetos a relacional, y por lo tanto promueve la modificabilidad, pero posiblemente influye negativamente sobre el desempeño.

 

Referencias

[1] Alexander, Ishikawa & Silversteing, “A Pattern Language: Towns, Buildings, Construction”, Oxford University Press, 1977.

[2] Gamma, Helm, Johnson & Vlissides, “Design Patterns: Elements of Reusable Object-Oriented Software”, Addison-Wesley Professional, 1994.

[3] Bass, Clements & Kazman, “Software Architecture in Practice, 2nd Edition”, Addison-Wesley Professional, 2003.

Bio

El Dr. Humberto Cervantes es profesor-investigador en la UAM-Iztapalapa. Además de realizar docencia e investigación dentro de la academia en temas relacionados con arquitectura de software, realiza consultoría y tiene experiencia en la implantación de métodos de arquitectura dentro de la industria de desarrollo nacional. Ha recibido diversos cursos de especialización en el tema de arquitectura de software en el Software Engineering Institute, y está certificado como ATAM Evaluator y Software Architecture Professional por parte del mismo. www.humbertocervantes.net