66
PATRONES PARA MEJORAR LA EFICIENCIA Y ESCALABILIDAD DE ARQUITECTURAS DISTRIBUIDAS 1. Introducción El proceso del desarrollo de sistemas de software ha ido en aumento en los últimos años, demandando la construcción de grandes y complejos sistemas que requieren la combinación de diferentes tecnologías y plataformas de hardware y software para alcanzar la funcionalidad requerida. El diseño e implementación ha pasado de una concepción monolítica y uniforme a una visión heterogénea y distribuida. El proceso de desarrollo se ha ido convirtiendo poco a poco en una labor de ingeniería, poniendo de manifiesto la relevancia de un estudio específico de la estructura del software. Actualmente la elaboración de especificaciones, el diseño del sistema, construcción de prototipos, integración y pruebas, forman parte de la Ingeniería del Software respondiendo a la creación de nuevos modelos, notaciones, técnicas y métodos. Dentro de esta orientación se enmarca el creciente interés al estudio, análisis y descripción de la estructura del software dando lugar a aspectos arquitectónicos del mismo. Estos aspectos se refieren a todo lo relativo a la estructura de alto nivel de los sistemas: su organización en subsistemas y la relación entre ellos; la construcción de aplicaciones con reutilización de otras existentes y desarrollo de productos que presentan una arquitectura común. En consecuencia, el modelado de una arquitectura a nivel conceptual permite al diseñador decidir cuestiones que tendrán influencia a lo largo de todo el ciclo de vida de la aplicación. 2. Arquitectura de Software Hoy en día las organizaciones hacen uso de sistemas de software complejos, de gran tamaño y combinando distintas tecnologías y plataformas de hardware. Esto exige a los desarrolladores de software diseñar muy cuidadosamente la arquitectura bajo la cual funcionan sus sistemas, ya que las decisiones que se tomen tendrán gran influencia a lo largo de todo el ciclo de vida de la aplicación.

Patrones Para Mejorar La Eficiencia y Escalabilidad de Arquitecturas Distribuidas

Embed Size (px)

Citation preview

PATRONES PARA MEJORAR LA EFICIENCIA Y ESCALABILIDAD DE ARQUITECTURAS DISTRIBUIDAS

1. Introducción

El proceso del desarrollo de sistemas de software ha ido en aumento en los últimos años, demandando la construcción de grandes y complejos sistemas que requieren la combinación de diferentes tecnologías y plataformas de hardware y software para alcanzar la funcionalidad requerida. El diseño e implementación ha pasado de una concepción monolítica y uniforme a una visión heterogénea y distribuida.

El proceso de desarrollo se ha ido convirtiendo poco a poco en una labor de ingeniería, poniendo de manifiesto la relevancia de un estudio específico de la estructura del software. Actualmente la elaboración de especificaciones, el diseño del sistema, construcción de prototipos, integración y pruebas, forman parte de la Ingeniería del Software respondiendo a la creación de nuevos modelos, notaciones, técnicas y métodos.

Dentro de esta orientación se enmarca el creciente interés al estudio, análisis y descripción de la estructura del software dando lugar a aspectos arquitectónicos del mismo. Estos aspectos se refieren a todo lo relativo a la estructura de alto nivel de los sistemas: su organización en subsistemas y la relación entre ellos; la construcción de aplicaciones con reutilización de otras existentes y desarrollo de productos que presentan una arquitectura común. En consecuencia, el modelado de una arquitectura a nivel conceptual permite al diseñador decidir cuestiones que tendrán influencia a lo largo de todo el ciclo de vida de la aplicación.

2. Arquitectura de Software

Hoy en día las organizaciones hacen uso de sistemas de software complejos, de gran tamaño y combinando distintas tecnologías y plataformas de hardware. Esto exige a los desarrolladores de software diseñar muy cuidadosamente la arquitectura bajo la cual funcionan sus sistemas, ya que las decisiones que se tomen tendrán gran influencia a lo largo de todo el ciclo de vida de la aplicación.

Si bien no hay una definición oficial de Arquitectura de Software, podemos decir que abarca todo lo relativo a la estructura de alto nivel de los sistemas: su organización en subsistemas y la relación entre ellos. Según David Garlan la Arquitectura de Software establece un puente entre el requerimiento y el código. A su vez el documento de IEEE Std 1471-2000 define: “La Arquitectura de Software es la organización fundamental de un sistema encarnada en sus componentes, las relaciones entre ellos y el ambiente y los principios que orientan su diseño y evolución”.

Es decir, la arquitectura brinda una visión global del sistema. Esto permite entenderlo, organizar su desarrollo, plantear la reutilización del software y hacerlo evolucionar. Se puede ver que la noción clave de la arquitectura es la organización y está relacionada con aspectos de rendimiento, usabilidad, reutilización, limitaciones económicas y tecnológicas.

Cada paradigma de desarrollo exige vistas, de las cuales, hay por lo menos tres que son esenciales en cualquier arquitectura:

- La visión estática: describe cuáles son los componentes de la arquitectura.

- La visión funcional: describe qué hace cada componente.

- La visión dinámica: describe cómo se comportan los componentes a lo largo del tiempo y como interactúan entre sí.

Las vistas de una arquitectura pueden formularse por medio de uno o varios lenguajes. El más obvio es el lenguaje natural, pero también existen otros como los diagramas de estado, los diagramas de flujo de datos, etc.. Existe cierta aceptación en el uso de UML (Unified Modeling Language, lenguaje unificado de modelado) como único lenguaje para todos los modelos o vistas.

Según Sahw y Garlan existen un conjunto de propiedades que se deben especificar como parte del diseño arquitectónico:

- Propiedades estructurales. Este aspecto de la representación del diseño arquitectónico define los componentes de un sistema y la forma en que se empaquetan e interactúan unos con otros.

- Propiedades extra-funcionales. La descripción del diseño arquitectónico debería ocuparse de cómo consigue la arquitectura del diseño los requisitos de rendimiento, capacidad, fiabilidad, seguridad, adaptabilidad y otras características del sistema.

- Familias de sistemas relacionados. El diseño arquitectónico debería tener la capacidad de utilizar bloques de construcción arquitectónica reutilizados.

¿Qué son los patrones?

Los patrones son una disciplina de resolución de problemas en la ingeniería del software que ha surgido con mayor énfasis en la comunidad de orientación a objetos, aunque pueden ser aplicados en cualquier ámbito de la informática y las ciencias en general.

Los patrones de software permiten que se reutilice tanto el diseño como la arquitectura, adoptando estructuras estáticas y dinámicas de soluciones exitosas en la solución de nuevos problemas.

Las técnicas generales para la arquitectura de software no apuntan a la solución de problemas específicos. Varios de los métodos existentes de análisis y diseño fallan a este nivel, ya que solamente proveen técnicas generales para construir software. La creación de arquitecturas específicas sigue basada en la intuición y experiencia. Los patrones son bloques de construcción mental útiles para proceder con aspectos de diseño limitados y específicos en el momento del desarrollo de un sistema de software, su concepto dominante es la reutilización.

Origen e historia de patrones

En la década del 90 fue la época del surgimiento de los patrones, definidos en dos orientaciones diferentes, las cuales fueron plasmadas en dos libros fundamentales sobre el tema, “Design Patterns” escrito por la Banda de los Cuatro (GoF)1 [Gamma95] en 1995 y el libro “Pattern-Oriented Software Architecture, A System of patterns”2 de la serie POSA [Buschmann+96] en 1996. El primero de ellos presenta un conjunto de patrones de diseño de software bajo el paradigma de la orientación a objetos, mientras que el segundo despliega un marco levemente más ligado a la Arquitectura de Software. Este surgimiento no ha hecho más que expandirse desde esos años.

Aunque el arquitecto Christopher Alexander hacía referencia a patrones de edificios y urbanos, lo que él proponía se puede aplicar de igual forma a patrones de software, donde las soluciones se plantean en términos de componentes, interfaces y relaciones en lugar de paredes y puertas.

Años más tarde, las ideas llegaron por fin a la informática. Si bien el concepto de arquitectura implícita en el trabajo actual con patrones, está más cerca de la implementación, la reutilización de patrones guarda estrecha relación con la tradición del diseño concreto orientado a objetos.

En pos de la Arquitectura de Software en estos años se homogeneizó la terminología utilizada, se tipificaron los estilos arquitectónicos, patrones arquitectónicos, se elaboraron lenguajes de descripción de arquitectura y también se consolidaron las vistas arquitectónicas.

Estilos Arquitectónicos

Un estilo arquitectónico es una lista de tipos de componentes que describen los patrones o las interacciones a través de ellos. Un estilo afecta a toda la arquitectura de software y puede combinarse en la propuesta de solución.

Los estilos ayudan a un tratamiento estructural que concierne más bien a la teoría, la investigación académica y la arquitectura en el nivel de abstracción más elevado, expresando la arquitectura en un sentido más formal y teórico.

Una vez que se han identificado los estilos, es lógico y natural pensar en reutilizarlos en situaciones semejantes que se presenten en el futuro.

Cuando se habla de una arquitectura en tres capas, o una arquitectura cliente-servidor, tácitamente se está haciendo referencia a una clasificación de las posibles configuraciones disponibles, en cuyo contexto adquiere un significado distintivo. No tiene sentido hablar de estilos si no se clarifica cuál es la tipología total en la que cada uno de ellos engrana. Definir una arquitectura como, por ejemplo, orientada a servicios ciertamente la tipifica, la distingue, la singulariza.

Existe una clasificación de familias de estilos, entre los que podemos destacar:

- Estilos de Flujo de Datos: Esta familia de estilos destaca la reutilización y la modificabilidad. Es apropiada para sistemas que implementan transformaciones de datos en pasos sucesivos.

- Estilos Centrados en Datos: Pone énfasis en la integridad de los datos. Son útiles para sistemas que se centran en el acceso y actualización de datos.

- Estilos de Llamada y Retorno: Pone mayor atención sobre la modificabilidad y la escalabilidad del sistema. Son estilos que se utilizan para sistemas en gran escala.

- Estilos de Código Móvil: Su mayor interés está en la portabilidad. Como ejemplo están los intérpretes.

Estilo Vs Patrones

Los estilos expresan componentes y las relaciones entre éstos, con las restricciones de su aplicación y la composición asociada, así como también las reglas para su construcción. Así mismo, se considera como un tipo particular de estructura fundamental para un sistema de software, junto con un método asociado que especifica cómo construirlo.

Por otra parte, los patrones arquitectónicos capturan existencia, experiencia comprobada en el desarrollo del software y ayudan a promover buenas prácticas de diseño.

Cada patrón es específico a un problema recurrente en el diseño e implementación de un sistema de software. Un patrón, como ya se ha comentado, se considera un par problema – solución, resultado de la experiencia en el diseño de arquitecturas de sistemas y propone los patrones arquitectónicos como descripción de un problema particular y recurrente de diseño, que aparece

en contextos de diseño específico, y presenta un esquema genérico demostrado con éxito para su solución. El esquema de solución se especifica mediante la descripción de los componentes que la constituyen, sus responsabilidades y desarrollos, así como también la forma como éstos colaboran entre sí.

Los estilos y patrones ayudan al arquitecto a definir la composición y el comportamiento del sistema de software. Se puede afirmar que una combinación adecuada de ellos permite alcanzar los requerimientos de calidad esperados.

Lenguajes de Descripción Arquitectónicas

Como fue indicado anteriormente, la Arquitectura del Software tiene como objetivo el conocimiento, análisis y reutilización de la arquitectura de sistemas de software. Para explicitar dicha arquitectura, se requiere hacer uso de algún lenguaje. Los Lenguajes de Descripción Arquitectónicas (ADLs) son lenguajes que se focalizan en la descripción de la estructura de alto nivel de una aplicación pero a su vez permiten un nivel de detalle suficiente para describir propiedades de interés de dicha aplicación. De esta manera es posible comprobar, ya desde los primeros pasos del desarrollo de un sistema, si éste cumple o no determinados requisitos.

De un modo general, se puede decir que los ADLs pretenden que se pueda analizar visualmente el sistema sin sufrir el aprendizaje de una sintaxis especializada. Dicha herramienta ha sido consensuada y estandarizada siendo de propósito general, adaptable a soluciones de cualquier estilo arquitectónico.

Los conceptos tratados en una descripción arquitectónica son:

- Componentes. Representan unidades de computación o de almacenamiento de datos.

- Conectores. Modelan las interacciones entre componentes y las reglas que se aplican a dichas interacciones.

- Configuraciones arquitectónicas. Grafos de componentes y conectores que describen la estructura arquitectónica del sistema.

El objetivo de los ADLs es describir la interfaz de cada componente, no su comportamiento interno, formando de esta manera sistemas más grandes. La descripción de la interfaz incluye los métodos que ofrece o requiere el componente, información sobre la funcionalidad del mismo, los patrones de interacción que utiliza en su funcionamiento, y otras características diversas.

Los requisitos que deben cumplir los ADLs son los siguientes:

- Composición: Describir el sistema como una composición de partes.

- Configuración: Describir la arquitectura independientemente de los componentes.

- Abstracción: Describir los roles abstractos que juegan los componentes.

- Reutilización: Permitir reutilizar componentes, conectores, y arquitecturas.

- Heterogeneidad: Permitir combinar descripciones heterogéneas.

- Análisis: Permitir diversas formas de análisis de la arquitectura.

Existen varios ejemplos de ADLs, entre los que se encuentran: Unicon, Wright, Darwin, Rapide, etc. cada uno con sus propias características.

Framework

Un framework es un diseño reutilizable del sistema completo o de alguna de sus partes y se expresa mediante un conjunto de clases abstractas y la forma de interactuar de sus instancias.

Un simple framework puede involucrar a muchos patrones de diseño; es decir, estos patrones son más pequeños que los framework, lo que implica, que los patrones son menos abstractos que ellos, son elementos microarquitectónicos de los frameworks.

3. Patrones

Clasificación de Patrones según la naturaleza del problema

Considerando algunos de los patrones existentes se observa que ellos pueden cubrir varios rangos de escala y abstracción:

- Estructurar un sistema de software dentro de subsistemas.

- Soportar el refinamiento de subsistemas y componentes o la relación entre ellos.

- Ayudar a implementar aspectos particulares de diseño en un lenguaje de programación específico.

Para refinar dicha escala podemos agrupar los patrones que representan un rango similar de abstracción en tres categorías:

- Patrones Arquitectónicos

- Patrones de Diseño

- Idioms

Patrones Arquitectónicos

Los patrones arquitectónicos son plantillas que describen los principios estructurales globales que construyen las distintas Arquitecturas de Software viables.

Plantean una organización estructural fundamental para un sistema de software, expresando un conjunto de subsistemas predefinidos, especificando responsabilidades y organizando las relaciones entre ellos.

La selección de un patrón arquitectónico es además una decisión fundamental de diseño cuando se desarrolla un sistema de software.

Patrones de Diseño

Un patrón de diseño provee un esquema para refinar componentes de un sistema de software y la forma en que se relacionan entre sí. Describe una estructura generalmente recurrente de comunicación de componentes que resuelve un problema de diseño general dentro de un contexto particular.

Los patrones de diseño son patrones de granularidad media, ya que tienen menor nivel de abstracción que los patrones arquitectónicos pero tienden a ser independientes de un lenguaje de programación en particular o de un paradigma de programación.

La aplicación de un patrón de diseño no tiene un efecto fundamental en la estructura del sistema de software global, pero tiene gran ingerencia sobre la arquitectura de un subsistema.

Idioms

Los Idioms están relacionados con la implementación de diseño de problemas particulares. Un Idiom es un patrón de bajo nivel específico para un lenguaje de programación. Describe como implementar aspectos particulares de componentes o las relaciones entre ellos usando las características dadas por el lenguaje.

Los Idioms representan el nivel más bajo de patrones y direccionan tanto aspectos de diseño como de implementación.

A continuación se presenta una clasificación de patrones según la categoría del problema

Características de un buen patrón

Según James Coplien, un buen patrón es aquel que:

- Resuelve un problema, ya que representa una solución, no suposiciones o estrategias abstractas.

- Captura soluciones a problemas, que han sido repetidamente probadas y no son solo teorías o especulaciones.

- Genera una solución a un problema indirectamente (un enfoque necesario para los problemas de diseño más difíciles). - No describe módulos sino estructuras y mecanismos de relación entre ellas. - Pone especial atención a la estética y a las utilidades. Tiene un componente humano significante.

Esquema de un patrón

Todo patrón se representa con un esquema de tres partes: Contexto – Problema – Solución.

El esquema denota una regla que establece una relación entre un contexto dado, un cierto problema que tiene lugar en ese contexto y una solución apropiada al problema.

Contexto: El contexto extiende la dicotomía problema-solución describiendo la situación en la cual ocurre el problema.

Es difícil especificar el contexto correcto para un patrón, siendo prácticamente imposible determinar todas las situaciones, tanto generales como particulares en la cual puede ser aplicado un patrón. Un acercamiento más pragmático es listar todas las situaciones conocidas que pueden ocurrir donde un problema es direccionado por un patrón en particular. Esto no garantiza que se cubran todas las situaciones en las cuales pueda ser relevante el patrón pero al menos nos da una guía valuable.

Problema: Esta parte del esquema de descripción de patrón representa el problema que nace repetidamente en un contexto dado. Comienza con su especificación general, determinando cual es el problema en concreto que se debe resolver y tratando de balancear sus fuerzas.

Solución: La solución parte de un patrón que muestra como resolver un problema recurrente, o como balancear mejor las fuerzas asociadas a él.

El siguiente diagrama resume un esquema, el cual captura la esencia de un patrón independientemente de su dominio.

Categorías de Patrones

Patrones Arquitectónicos

Los patrones arquitectónicos representan el nivel más alto dentro del sistema de patrones y expresan el esquema de la estructura fundamental de la organización para sistemas de software. Proveen un conjunto de subsistemas predefinidos, especifican sus responsabilidades e incluyen reglas y guías para organizar las relaciones entre ellos. Cada patrón ayuda a lograr una propiedad específica del sistema global como es la adaptabilidad de la interfaz de usuario.

Los patrones que dan soporte a propiedades similares pueden ser agrupados en las siguientes categorías:

- Del fango a la estructura. Ayudan a evitar un “mar” de componentes u objetos. En particular apoyan una descomposición controlada de una tarea del sistema global en subtareas cooperantes. Esta categoría incluye los patrones Layers, Pipes and Filters y Blackboard. - Sistemas Distribuidos: incluye el patrón Broker y hace referencia a dos patrones que se encuentran en otras categorías, Microkernel y Pipes and Filters. El patrón Broker provee una infraestructura completa para aplicaciones distribuidas. Los patrones Microkernel y Pipes and Filters consideran la distribución como un concepto secundario y están ubicados bajo sus respectivas categorías primarias. - Sistemas Interactivos: En esta categoría entran dos patrones el Model-View-Controller (MVC) y el Presentation–Abstraction–Control (PAC). Ambos apoyan la estructuración de sistemas de software que ofrecen la interacción usuario-computadora. - Sistemas Adaptables: Los patrones Reflection y Microkernel apoyan fuertemente la extensión de aplicaciones y su adaptación a desenvolverse con la tecnología y cambios en los requisitos funcionales.

Hay puntos que son recomendables para tener en cuenta al momento de elegir un patrón arquitectónico:

• Es útil explorar varias alternativas antes de decidir sobre un patrón arquitectónico específico.

• Diferentes patrones arquitectónicos implican diferentes consecuencias, aún si direccionan al mismo o problemas similares.

• Muchos sistemas de software no pueden ser estructurados de acuerdo a un patrón arquitectónico simple, dando soporte a requerimientos sistema que solamente pueden ser solucionados mediante la aplicación de diferentes patrones arquitectónicos.

Patrones de Diseño

Los patrones de diseño son soluciones bien documentadas que los desarrolladores emplean para dar solución a nuevos problemas apoyados en la experiencia de haberlas utilizado con éxito en el pasado. Los profesionales identifican partes de un problema que son análogos a otros problemas que han resuelto anteriormente. Luego, retoman la solución utilizada y la generalizan. Por último, adecúan la solución general al contexto de su problema actual.

Dentro de los patrones de diseño existen variaciones según su nivel de granularidad y abstracción, lo que permite clasificarlos bajo dos criterios: Propósito, refleja qué hace un patrón teniendo en cuenta si es de Creación, Estructural o de Comportamiento; y Ámbito, especifica si un patrón se aplica primariamente a una clase o a un objeto.

- De Creación: abstrae el proceso de instanciación de objetos, su misión es permitir construir sistemas independientes de la forma de creación, composición o representación de objetos. Un patrón de creación de clases utiliza la herencia para variar la clase que es instanciada, un ejemplo de este tipo de patrón es Factory Method. Un patrón de creación de objetos delega la instanciación en otro objeto, por ejemplo el patrón Builder.

- Estructural: controla como se componen las clases u objetos en la construcción de estructuras mayores. Un patrón estructural de clases utiliza la herencia para componer interfaces o implementaciones, por ejemplo el patrón Adapter. Un patrón estructural de objetos describe la forma en que se componen objetos para obtener nueva funcionalidad, además se añade la flexibilidad de cambiar la composición en tiempo de ejecución, lo cual no es posible con la

composición de clases estáticas, como representante de este tipo de patrón se puede mencionar al patrón Composite. - De Comportamiento: se relaciona con algoritmos, la forma en la que interactúan las clases u objetos y la asignación de responsabilidades entre ellos. Los patrones de comportamiento de clases utilizan la herencia para distribuir el comportamiento entre las clases, se puede citar como ejemplo el patrón Command. Por su parte los patrones de comportamiento de objetos cooperan como un grupo de objetos interconectados para realizar una tarea que un solo objeto no puede realizar por sí solo, un ejemplo es el patrón

Idioms

En contraste con los patrones de diseño, los cuales se orientan hacia las arquitecturas generales principales, los idioms describen cómo resolver problemas de implementación específicos en un lenguaje de programación determinado. Los Idioms pueden también realizar directamente la implementación concreta de un patrón de diseño particular.

Los idioms se aproximan o se solapan con áreas que son, por lo general, regidas por guías de programación.

4. Patrones Arquitectónicos

La selección de un patrón arquitectónico debería ser conducida por las propiedades generales de la aplicación a estudiar. Es útil explorar varias alternativas antes de decidir sobre un patrón arquitectónico específico. Por ejemplo el patrón PAC y el MVC ambos se prestan para aplicaciones interactivas. En forma similar los patrones Reflection y Microkernel apoyan la adaptación de sistemas de software a la evolución de requisitos.

Del Fango a la Estructura

En esta categoría se encuentran los patrones que ayudan a evitar un “mar” de componentes u objetos apoyando una descomposición controlada de una tarea del sistema global en subtareas cooperantes.

Dentro de esta clasificación de patrones arquitectónicos se encuentran diferentes tipos de patrones que proporcionan subdivisiones de alto nivel del sistema: Layers, Pipes and Filters y Blackboard donde:

El patrón Layers Ayuda a estructurar aplicaciones que pueden ser descompuestas en grupos de subtareas, en el que cada grupo pertenece a un nivel particular de abstracción.

Estructura

El patrón Layers tiene varias ventajas:

o Reusabilidad. Si una capa individual incluye una abstracción bien definida y tiene una interfaz bien definida y documentada, la capa puede ser reusada en múltiples contextos.

o Estandarización. Niveles de abstracción claramente definidos y comúnmente aceptados habilitan el desarrollo de tareas e interfaces estandarizadas. Implementaciones diferentes de la misma interfaz pueden ser usadas en forma indistinta. Esto permite usar productos de diferentes vendedores en distintas capas.

o Portabilidad. Las interfaces estandarizadas entre capas limitan el efecto de cambios de código a la capa a modificar. Los cambios de hardware, del sistema operativo y todo lo que afecta solamente a una capa, se puede modificar sin alterar al resto de las capas. Esto permite la portabilidad del sistema, y testeo independiente de las capas.

o Cambiabilidad. La implementación de capas individuales puede ser reemplazada por una implementación semánticamente equivalente sin grandes esfuerzos, por ejemplo, cambiar o agregar un hardware. Un nuevo dispositivo de E/S, puede ser puesto en operación instalando el driver correcto. Las capas superiores no se verán afectadas por el

El patrón Layer también impone desventajas:

o Baja eficiencia. Una arquitectura en capas es usualmente menos eficiente que una estructura monolítica. Si servicios de alto nivel en capas superiores necesitan obligatoriamente de las capas inferiores, todo dato relevante debe ser transferido a través de varias capas intermedias, y puede llevar mucho tiempo. La comunicación de protocolos, por ejemplo, transforman los mensajes de alto nivel mediante el agregado de cabeceras e información de control.

o Trabajo innecesario. Capas inferiores pueden necesitar hacer tareas que no han sido solicitadas para brindar un determinado servicio a una capa superior. Este trabajo excesivo tiene un impacto negativo en la performance.

o Dificultad al establecer la correcta granularidad de las capas. Una arquitectura de capas con muy pocas capas no explota completamente la potencial reusabilidad, cambiabilidad y portabilidad de este patrón. Por otro lado, también muchas capas introducen complejidad innecesaria de sobrecarga en la separación y la transformación de argumentos y valores de retorno. La decisión de la granularidad de las capas y la asignación de tareas es dificultosa, pero es indispensable para lograr calidad en la

Ejemplo

Un ejemplo típico donde se utiliza el patrón Layers es en la estructura del protocolo de comunicación OSI. Los diseñadores hacen uso de varios subprotocolos y los colocan en capas. Esta es una arquitectura en donde hay varios niveles de comunicación, partiendo desde el hardware pasando por la comunicación punto a punto y llegando a los protocolos de aplicaciones. Estos niveles a su vez se dividen en subniveles y llevándolos cada uno a capas diferentes. Cada capa lleva a cabo una tarea específica para la comunicación y utiliza los servicios que le brinda su capa inmediata inferior. La arquitectura estaría dividida en las siguientes capas:

Donde cada una de ellas se comunica solo con su capa inmediatamente inferior para solicitarle servicios, y con su capa inmediatamente superior para brindarle servicio, imposibilitando la comunicación entre capas no adyacentes. Mantener la independencia entre capas de esta manera, permite realizar modificaciones en una capa sin que esto afecte al resto de las capas. La estratificación en capas es considerada una mejor práctica que la implementación del protocolo como un bloque monolítico.

- El patrón Pipes and Filters provee una estructura para sistemas que procesan un flujo de datos. Cada paso del proceso esta encapsulado en un componente filter. Los datos se pasan a través de los pipes entre filters adyacentes. La combinación de filters permite construir familias de sistemas relacionados. Estructura

El patrón Pipes and Filters tiene las siguientes ventajas:

o No son necesarios archivos intermedios, pero es posible su utilización. Es posible calcular resultados usando programas separados sin pipes, guardando los resultados intermedios en archivos. El uso de este patrón quita la necesidad de archivos intermedios, pero permite investigar los datos intermedios usando la unión “T” en el pipeline.

o Flexibilidad por el intercambio de filters. Los filters tienen una interfaz simple que permite su intercambio fácilmente dentro del procesamiento del pipeline. o Flexibilidad por recombinación. Este es el mayor beneficio, combinado con la reusabilidad de los componentes filters, permitiendo la creación de nuevos procesos pipelines reestructurando los

filters o agregando uno nuevo. Un pipeline sin una fuente o un almacenamiento de datos puede ser embebido como un filter dentro de un pipeline más grande. o Reusabilidad. Permite la recombinación de filters facilitando el reuso de sus componentes. o El prototipado rápido de los pipelines. Las Ventajas precedentes hacen esto más fácil para el prototipo de un sistema de procesamiento de datos de los filters existentes. Después que se tiene la implementación de la función del sistema principal usando un pipeline, se puede optimizar incrementalmente. o Eficiencia del procesamiento en paralelo. Si cada filter en un pipeline produce y consume datos incrementalmente pueden realizar sus funciones en paralelo.

Aplicar patrones Pipes and Filters imponen algunas desventajas:

o Compartir el estado de la información es caro o poco flexible. Aplicar el patrón Pipes and Filters es ineficiente si las fases de procesamiento necesitan compartir una gran cantidad de datos globales

o Desventaja en el procesamiento en paralelo. Esto se debe a varias razones:

• El costo de transferir datos entre filters puede ser relativamente alto comparado con el costo de realizar cómputos en un solo filter.

• Algunos filters consumen todas sus entradas antes de producir cualquier salida, Esto puede ser debido a que el filter esta mal codificado.

o Manejo de errores. El manejo de errores es una gran debilidad del patrón Pipes and Filters. Se debería por lo menos definir una estrategia común para el reporte de errores y usarse a lo largo de todo el sistema.

Ejemplo

Unix ha popularizado el paradigma pipe and Filter. El comando shell y la disponibilidad de varios programas filters hacen de Unix un sistema popular. Como un sistema para diseñadores de software, tareas frecuentes tales como la compilación de un programa y la creación de documentación son realizadas por pipelines en un sistema Unix tradicional. La flexibilidad de los pipes de Unix hizo del sistema operativo una plataforma conveniente para el reuso binario de programas filters y para la integración de aplicación.

- El patrón Blackboard es útil para problemas en los cuales no se conoce ninguna estrategia de solución determinística. En este patrón varios subsistemas especializados ensamblan sus conocimientos para construir una solución posiblemente parcial o aproximada.

La forma en que el patrón Blackboard descompone el problema y aplica el conocimiento, aporta las siguientes ventajas:

o Experimentación. En dominios en que no existe alguna posible solución y una búsqueda completa del espacio de la solución no es factible, el patrón Blackboard experimenta con la mayor cantidad posible de algoritmos diferentes, y también permite intentar con diferentes heurísticas de control.

o Cambiabilidad y mantenibilidad. La arquitectura Blackboard soporta la cambiabilidad y mantenibilidad porque las fuentes de conocimiento individuales, el algoritmo de control y la estructura de datos central están estrictamente separados.

o Reusabilidad. Las fuentes de conocimiento son independientes para ciertas tareas. Una arquitectura Blackboard ayuda haciéndolas reusables. Los requisitos previos para reusar son que la fuente de conocimiento y el sistema Blackboard subyacente entiendan el mismo protocolo y datos.

o Tolerancia a fallos y robustez. En una arquitectura Blackboard todos los resultados son sólo hipótesis. Sólo sobrevivirán aquellas que son fuertemente soportadas por los datos y otras hipótesis.

El patrón Blackboard tiene algunas desventajas:

o Dificultad de testeo. Los cómputos de este patrón no siguen un algoritmo determinístico, por lo tanto sus resultados no son a menudo reproducibles. Además, hipótesis erróneas son parte del proceso de solución.

o No se garantiza ninguna buena solución. Normalmente puede resolver correctamente sólo un cierto porcentaje de las tareas dadas.

o Dificultad de establecer una buena estrategia de control. La estrategia de control no puede diseñarse de una manera íntegra, y requiere un acercamiento experimental. o Baja eficiencia. Padece el exceso computacional del rechazo de las hipótesis erróneas. o Alto esfuerzo de desarrollo. La mayoría de estos sistemas toman años para evolucionar. Se atribuye esto a los dominios mal estructurados del problema y una programación basada en prueba/error para definir estrategias de control y fuentes de conocimiento. o No soportaparalelismo. La arquitectura Blackboard no provee la ejecución en paralelo. El acceso concurrente a los datos centrales en la pizarra se debe sincronizar.

Ejemplo

Se ha usado tradicionalmente el patrón Blackboard para aplicaciones que requieren complejas interpretaciones de procesamiento de señales, como también en sistemas que involucran el acceso compartido a datos con agentes escasamente acoplados.

Un ejemplo de uso es un sistema HASP diseñado para detectar submarinos enemigos. En este sistema, un hidrófono (dispositivo que captura las ondas acústicas transmitidas en el agua) muestra en el monitor un área del mar colectando señales sonoras. Un sistema Blackboard interpreta estas señales. El sistema HASP es un sistema basado en eventos en el sentido que la ocurrencia de un evento particular implica que nueva información este disponible. La pizarra es

usada como una “tabla de situación” que evoluciona con el tiempo. Como la información se recolecta continuamente, hay información redundante como también nueva y diferente. HASP trata con múltiples flujos de entrada. Además el bajo nivel de datos del hidrófono acepta descripciones de alto nivel de la situación recogida de inteligencia u otras fuentes.

Sistemas Distribuidos

Hoy día, aún las pequeñas compañías usan sistemas distribuidos. ¿Pero cuáles son las ventajas de los sistemas distribuidos que los hacen interesantes?

Economía. Redes de computadoras que incorporan PCs y workstations ofrecen una mejor relación costo/performance que un mainframe.

Performance y Escalabilidad. Las aplicaciones distribuidas, usan recursos disponibles en toda la red. La performance puede mejorar enormemente si se utiliza en forma combinada, el poder de cómputo de varios nodos de red. Además, multiprocesadores y redes son fácilmente escalables.

Distribución inherente. Algunas aplicaciones son naturalmente distribuidas, por ejemplo aplicaciones de base de datos en un modelo Cliente-Servidor.

Fiabilidad. En la mayoría de los casos, una máquina en una red o una CPU en un sistema multiprocesador puede dejar de funcionar sin afectar el resto del sistema. Los

nodos centrales como los servidores de archivos son las excepciones a esto, pero puede protegerse con sistemas auxiliares.

Los sistemas distribuidos, sin embargo tienen un inconveniente significante, necesitan software totalmente diferente a los sistemas centralizados.

Tres modelos relacionados a sistemas distribuidos en esta categoría:

- El patrón Pipes and Filters mantiene una estructura para sistemas que procesan un flujo de datos. Cada paso del proceso es encapsulado en un componente filter. Los datos son pasados a través de pipes entre filters adyacentes. La recombinación de filters permite construir familias de sistemas relacionados.

Este patrón se utiliza más por estructurar la funcionalidad central de una aplicación que por su naturaleza distribuida, es por ello que lo ubicamos junto al patrón Layers en la clasificación anterior.

- El patrón Microkernel se aplica a sistemas de software que tienen la necesidad de adaptar el sistema a requerimientos cambiantes. Este patrón separa la funcionalidad central mínima de la funcionalidad extendida y las partes específicas del cliente. El microkernel también sirve como un socket para comunicar estas partes y coordinar su colaboración. Este patrón es descripto más adelante en este capítulo dentro de la clasificación de patrones adaptables.

- Los sistemas Microkernel emplean una arquitectura Cliente-Servidor en los cuales clientes y servidores corren sobre el componente microkernel. El mayor beneficio de estos sistemas, está en el diseño apto para la adaptación y cambio.

- El patrón Broker puede usarse para estructurar sistemas de software distribuidos con componentes desarticulados que se comunican mediante invocación remota de servicios. Un

componente broker es responsable de coordinar la comunicación (remitir las demandas, transmitir los resultados y excepciones).

Estructura

El patrón arquitectónico Broker comprende seis tipos de componentes participantes:

Un componente servidor que implementa objetos que exponen su funcionalidad a través de interfaces que consisten en operaciones y atributos. Estas interfaces son realizadas a través de un lenguaje de definición de interfaz (IDL). Las interfaces se agrupan por su funcionalidad semánticamente relacionada. Hay dos tipos de servidores:

• Servidores que ofrecen los servicios comunes a muchos dominios de aplicación.

• Servidores que implementan la funcionalidad específica por un solo dominio de aplicación o tarea.

Los componentes clientes son aplicaciones que acceden a los servicios de por lo menos un servidor.

El componente broker es responsable de la transmisión de solicitudes de los clientes a los servidores, como de la transmisión de respuestas y excepciones al cliente. También debe tener alguna forma de localizar al receptor de una solicitud basada en su único identificador del sistema. Un broker ofrece APIs a los clientes y a los servidores para que incluyan operaciones para registrar y para invocar los métodos del servidor.

Los componentes proxies del lado del cliente representan una capa entre el cliente y el broker. Esta capa adicional proporciona transparencia, el cliente ve a un objeto remoto como si fuese local. Los proxies permiten el ocultamiento de los detalles de implementación desde los clientes como:

• El mecanismo de comunicación inter-procesos usados para transferencia de mensaje entre clientes y broker.

• La creación y eliminación de bloques de memoria.

• El marshaling de parámetros y resultados.

Los componentes proxies del lado del servidor son generalmente análogos a los proxies del lado del cliente. La diferencia es que son responsables de la recepción de las solicitudes, desempaquetar los mensajes entrantes, realizar el unmarshaling de los parámetros, y llamar al servicio apropiado. Además se usan para realizar el marshaling de los resultados y excepciones antes de enviarlos al cliente.

Los componentes bridges son componentes opcionales usados para ocultar los detalles de implementación cuando dos broker ínter-operan en una red heterogénea. Si se transmiten las solicitudes sobre la red, los diferentes brokers tienen que comunicarse independientemente del uso de redes y sistemas operativos diferentes. Un bridge construye una capa que encapsula todos

estos detalles específicos del sistema. Este responde a un patrón de diseño cuya descripción se hace en el capítulo siguiente.

Consecuencias

El patrón arquitectónico Broker tiene algunas ventajas importantes:

o Transparencia. Como el broker es responsable de localizar un servidor usando un único identificador, los clientes no necesitan saber dónde se localizan los servidores. De igual forma, los servidores no se preocupan de la ubicación de los clientes que les realizan requerimientos ya que ellos reciben todas las solicitudes del componente broker local.

o Cambiabilidad y extensibilidad. Si los servidores cambian pero sus interfaces permanecen iguales, no tiene impacto funcional en los clientes. Modificando la implementación interior del broker pero no las API’s que él provee, no tiene otro efecto en los clientes y servidores más que cambios en la performance. El uso de proxies y bridges es una razón importante para facilitar la implementación de cambios.

o Portabilidad. El sistema Broker oculta detalles del sistema operativo y del sistema de red a clientes y servidores usando capas de indirección como las API’s, proxies y bridges. Cuando se requiere usar puertos es suficiente en la mayoría de los casos poner el puerto en el componente broker y sus APIs en una nueva plataforma y recompilar a los clientes y servidores, en estos casos, se recomienda estructurar los componentes del broker en capas

o Interoperabilidad. Diferentes sistemas Broker pueden interoperar si entienden un protocolo común para el intercambio de mensajes. Este protocolo es interpretado y manejado por bridges que son los responsables de traducir el protocolo específico del broker en el protocolo común y viceversa.

o Reusabilidad. Al construir nuevas aplicaciones clientes, frecuentemente están basadas en la funcionalidad de la aplicación en servicios existentes.

El patrón arquitectónico Broker impone algunas desventajas:

o Eficiencia restringida. Aplicaciones que usan una implementación Broker son normalmente más lentas que las aplicaciones cuya distribución del componente es estática y conocida. Sistemas que dependen directamente de un mecanismo concreto para la comunicación interproceso dan mejor

performance que una arquitectura Broker, porque el broker introduce capas de indirección que permite ser portable, flexible y cambiable.

o Baja tolerancia a fallos. Comparado con un sistema de software no distribuido, un sistema Broker puede ofrecer menos tolerancia a fallos. En caso que un servidor o un broker falle durante la ejecución de un programa, todas las aplicaciones que dependen del servidor o el broker son incapaces de continuar con éxito. Se puede aumentar la fiabilidad a través de la replicación de componentes.

El siguiente aspecto da ventajas así como también desventajas:

o Testing y debugging. Una aplicación cliente desarrollada de servicios probados es más robusta y fácil de testear. Sin embargo, realizar el testeo y la depuración de un sistema Broker es un trabajo tedioso debido a la cantidad de componentes involucrados.

Ejemplo

El patrón arquitectónico Broker es utilizado para especificar la arquitectura CORBA. CORBA es una tecnología orientada a objetos para objetos distribuidos sobre sistemas heterogéneos. Un lenguaje de definición de interfaz esta disponible para soportar la interoperabilidad de objetos clientes y servidores.

Sistemas Interactivos

Los sistemas actuales permiten un grado alto de interacción del usuario, generalmente, con la ayuda de interfaces de usuario gráficas. El objetivo es robustecer la utilidad de una aplicación. Estos sistemas proporcionan un acceso conveniente a sus servicios, lo cual permite a los usuarios aprender la aplicación y producir resultados rápidamente.

Se describen dos patrones que brindan una organización estructural fundamental para software de sistemas interactivos:

- El patrón Model-View-Controller (MVC) divide una aplicación interactiva en tres componentes. El modelo contiene la funcionalidad central y los datos. Las vistas despliegan el información al usuario. Los controladores se ocupan de las entradas del usuario. Las vistas y controladores juntos forman la interfaz de usuario. Un mecanismo de propagación de cambios asegura la consistencia entre la interfaz de usuario y el modelo.

Estructura

El Model-View-Controller (MVC) divide una aplicación interactiva en tres áreas: procesamiento, entrada y salida.

El modelo encapsula los datos centrales y tiene la funcionalidad de la aplicación. Es un componente totalmente independiente de las representaciones específicas de salidas o del comportamiento de la entrada.

Los controladores reciben la entrada, normalmente como eventos que codifican los movimientos del mouse o entrada del teclado. Los eventos son traducidos para servir a las demandas del modelo o las vistas. El usuario interactúa con el sistema solamente a través de los controladores.

Diferentes vistas presentan la información del modelo al usuario de distintas maneras. Pueden existir múltiples vistas de un mismo modelo, pero cada vista tiene una relación uno a uno con un controlador. Cada vista define un procedimiento de actualización que se activa por el mecanismo de propagación de cambios. Cuando es llamado el procedimiento de actualización, una vista recupera los valores de datos actuales del modelo para ser mostrados, y los pone en la pantalla

Consecuencias

La aplicación Model-View-Controller tiene varias ventajas:

o Múltiples vistas del mismo modelo. MVC separa al modelo de los componentes de la interfaz de usuario. Múltiples vistas pueden ser implementadas y usadas con un simple modelo.

Vistas sincronizadas. El mecanismo de propagación de cambios del modelo asegura que todos los observadores registrados son notificados de los cambios en los datos de la aplicación, en el momento correcto. Esto sincroniza todas las vistas y controladores dependientes. o Cambios en vistas y controladores. La separación conceptual de MVC permite intercambiar los objetos de las vistas y los controladores de un modelo incluso en tiempo de ejecución. o Cambiabilidad. Como el modelo es independiente de todo el código de la interfaz de usuario, exportar una aplicación MVC a una nueva plataforma no afectaría la funcionalidad central de la aplicación, solo es necesario la implementación conveniente para esa plataforma, de los componentes vistas y controladores. o El potencial del framework. Es posible basar una aplicación framework en este patrón.

Las desventajas de MVC son:

o Incremento de la complejidad. Seguir estrictamente la estructura MVC, no siempre es la mejor manera de construir una aplicación interactiva. También se sostiene que el uso de los componentes separados del modelo, la vista y el controlador para los menús y los elementos de texto simples implica un aumento de la complejidad sin ganar mucha flexibilidad.

o Excesivos número de actualizaciones. Si una sola acción del usuario implica varias actualizaciones, el modelo debe pasar por alto las notificaciones intermedias innecesarias de los cambios dado que no todas las vistas están interesadas en que el modelo propague cada una de las modificaciones.

o Conexión entre vistas y controladores. Controladores y vistas son componentes separados pero estrechamente relacionados que impiden su rehúso individual. Es improbable que una vista sea usada sin su controlador, o viceversa, con la excepción de vistas de sólo lectura que comparten un controlador que ignore todas las entradas.

o Acoplamiento de vistas y controladores con un modelo. Los componentes vista y controlador hacen llamadas directas al modelo. Esto implica que cambios en la interfaz del modelo probablemente rompan el código de vista y controlador. Este problema se magnifica si el sistema usa múltiples vistas y controladores.

o Ineficacia en la vista para acceder a los datos. Dependiendo de la interfaz del modelo, una vista puede necesitar hacer múltiples llamadas al modelo para obtener todos los datos a mostrar. Solicitar innecesariamente al modelo datos inalterados debilita la performance si las actualizaciones son frecuentes. Guardando en caché los datos dentro de la vista, mejora la performance.

Ejemplo

El patrón MVC se ve frecuentemente en aplicaciones interactivas, como por ejemplo, en un editor gráfico.

- El patrón Presentation-Abstraction-Control (PAC) define una estructura para los sistemas interactivos en forma de una jerarquía de agentes de cooperantes. Cada agente es responsable de un aspecto específico de la funcionalidad de la aplicación y consiste de tres componentes: presentación, abstracción y control. Esta subdivisión separa los aspectos de interacción de hombre-computadora de los agentes de su centro funcional y su comunicación con otros agentes.

Estructura

La responsabilidad principal del agente PAC de nivel superior es habilitar el modelo de datos global del software, mediante la funcionalidad de sus componentes

Los agentes PAC de nivel inferior representan un concepto semántico específico del dominio de la aplicación, también pueden implementar servicios del sistema. Este concepto semántico puede ser de tan bajo nivel como un objeto gráfico simple.

Los agentes PAC de nivel intermedio pueden cumplir dos papeles diferentes: composición y coordinación entre el agente de nivel inferior, por ejemplo al coordinar múltiples vistas de los mismos datos.

El patrón arquitectónico Presentation-Abstraction-Control tiene varias ventajas:

o Separar intereses. Agentes separados representan diferentes conceptos semánticos en el dominio de la aplicación. Cada agente mantiene su propio estado y datos en forma coordinada e independiente de otro agente.

o Cambiabilidad y extensibilidad. Los cambios dentro de los componentes presentación o abstracción de un agente no afectan a otros agentes en el sistema. Esto permite modificar individualmente el modelo de datos que está debajo de un agente o cambiar su interfaz de usuario. Nuevos agentes se integran fácilmente en una arquitectura PAC existente sin mayores cambios para el resto. Todos ellos se comunican entre sí a través de una interfaz predefinida.

o Multitarea. Los agentes pueden distribuirse fácilmente en diferentes hilos, procesos, o máquinas.

Las desventajas de este patrón son las siguientes:

o Incremento en la complejidad del sistema. La implementación de cada concepto semántico dentro de una aplicación como su propio agente, puede resultar en una estructura compleja del sistema.

o Complejidad en el componente control. Los componentes control son los mediadores de la comunicación entre los otros dos componentes de un agente, y entre los diferentes agentes. La calidad de la implementación del componente control es, por consiguiente, crucial para una colaboración eficaz entre agentes, y para la calidad global de la arquitectura del sistema.

o Decremento en la eficacia. El costo de comunicaciones entre agentes puede impactar en la eficacia del sistema. Por ejemplo, si un agente de nivel inferior recupera los datos del agente de nivel superior, todos los agentes del nivel intermedio de la jerarquía PAC estarán involucrados en este intercambio de datos.

Ejemplo

Considerar un sistema de información de censos que permite observar el crecimiento demográfico con representaciones proporcionales. Este ofrece una hoja de cálculos para el ingreso de los datos y varias formas de tablas y gráficos para representar las regiones. Los usuarios interactúan con el software a través de interfases gráficas. Sin embargo, diferentes versiones adaptan la interfaz del usuario según sus necesidades específicas. Por ejemplo una versión soporta la asignación de recursos dependiendo de las necesidades de cada región.

Por lo tanto, en este ejemplo se puede definir un agente PAC de nivel superior que provea el acceso al almacenamiento de datos subyacente del sistema. El almacenamiento de datos por si

mismo no es parte de la aplicación. Para realizar el nivel inferior se especifican cuatro agentes de PAC: un agente de la hoja de cálculo para los datos de entrada y tres agentes de vista para cada tipo de diagrama para representar los datos. La aplicación tiene un agente PAC intermedio que coordina a los tres agentes de vista del nivel inferior y los mantiene consistentes. El agente de la hoja de cálculo se conecta directamente al agente PAC de nivel superior. Los usuarios del sistema sólo interactúan con los agentes de nivel superior.

Sistemas Adaptables

Los sistemas evolucionan con el tiempo - se agrega nueva funcionalidad y los servicios van cambiando. Ellos deben soportar nuevas versiones de operar sistemas operativos, otras plataformas, otras interfases de usuarios y bibliotecas. Puede ser necesario adaptarlos a nuevos estándares o plataformas. También puede ser necesario proporcionar servicios que difieran de un cliente a otro.

En esta sección se describen dos modelos que ayudan al diseño para sistemas que se adaptan al cambio:

- El patrón Microkernel se aplica a los sistemas de software que deben adaptarse a requisitos cambiantes del sistema. Separa la funcionalidad central mínima de la funcionalidad extendida y las partes específicas de los clientes. El microkernel también sirve como socket para conectar extensiones y coordinar sus colaboraciones.

Estructura

El patrón de Microkernel ofrece algunas ventajas importantes:

o Portabilidad. Ofrece un alto grado de portabilidad por dos razones: En la mayoría de los casos no se necesita servidores externos o aplicaciones clientes portables si se traslada el sistema Microkernel a un nuevo ambiente de software o hardware. La migración del microkernel a un nuevo ambiente de hardware solamente requiere modificaciones en las partes dependientes del hardware. o Flexibilidad y Extensibilidad. Para implementar una vista adicional, se agrega un nuevo servidor externo. Para extender el sistema con capacidades adicionales solo requiere la suma o extensión de servidores internos. o Separación de política y mecanismo. Proporciona todos los mecanismos necesarios para habilitar a los servidores externos a implementar sus políticas, lo provoca que aumento en la

mantenibilidad y cambiabilidad de todo el sistema. Además, permite agregar nuevos servidores externos que implementen sus propias vistas especializadas. o Escalabilidad. Un sistema Microkernel distribuido es aplicable al desarrollo de sistemas operativos o sistemas de base de datos para redes de computadora, o multiprocesadores con memoria local. Si el sistema Microkernel trabaja sobre una red de máquinas, cuando se agrega una nueva máquina a la red, es sencillo escalar el sistema Microkernel a la nueva configuración. o Fiabilidad. Para lograr fiabilidad se requiere disponibilidad y tolerancia a fallos. Esta arquitectura permite correr el mismo servidor en más de una máquina, incrementando la disponibilidad y la tolerancia a fallos de una máquina o servidor. o Transparencia. En un sistema distribuido los componentes pueden distribuirse sobre una red de máquinas. En dicha configuración, la arquitectura Microkernel permite a cada componente acceder a otros componentes sin necesidad de conocer su ubicación.

La arquitectura Microkernel también tiene las siguientes desventajas:

o Performance. La performance es menor que la de un sistema del software monolítico diseñado para ofrecer una vista específica.

o Complejidad de diseño e implementación. Desarrollar un sistema Microkernel es una tarea no trivial. Además, la separación entre los mecanismos y las políticas requieren el conocimiento del dominio en profundidad y un esfuerzo considerable durante el análisis y el diseño.

Ejemplo

Por ejemplo si necesitáramos un sistema operativo que se pueda portar a otro hardware, y que ejecute programas escritos para sistemas operativos populares existentes (Unix, Windows), podríamos aplicar este patrón.

- El patrón Reflection provee un mecanismo para estructuras cambiantes y conductas dinámicas de sistemas de software. Apoya la modificación de aspectos fundamentales, como el tipo de estructuras y mecanismos de llamadas a función. En este patrón, una aplicación es dividida en dos partes. Un meta nivel que proporciona información sobre las propiedades seleccionadas del sistema y un nivel base que incluye la lógica de la aplicación. Su aplicación se construye sobre el meta nivel. Los cambios a la información contenidos en el meta nivel, afectan al comportamiento del nivel base.

El patrón Reflection proporciona las siguientes ventajas:

o Modificaciones no explicitas de código fuente. No se necesita tocar el código existente al modificar un sistema reflexivo. En cambio, se especifica un cambio llamando a una función del protocolo metaobjeto. Al extender el software, se pasa el nuevo código al meta nivel como un parámetro del protocolo metaobjeto. Este protocolo, por si mismo, es responsable de integrar sus solicitudes de cambio realizando modificaciones y extensiones al código del meta nivel y, si es necesario, recompila las partes cambiadas y realiza un enlace con la aplicación mientras está se esta ejecutando.

o Cambiar un sistema del software es fácil. El protocolo metaobjeto provee un mecanismo seguro y uniforme para realizar estos cambios. Esconde la complejidad interna de una aplicación a cambiar y toma el control sobre cada modificación.

o Soportar varios tipos de cambios. Los metaobjetos pueden encapsular cada aspecto del comportamiento del sistema, del estado y su estructura. Una arquitectura basada en este patrón soporta así potencialmente los cambios de casi cualquier tipo o escala.

El patrón Reflection tiene algunas desventajas significantes:

o Las modificaciones al meta nivel pueden causar daño. Aun el protocolo metaobjeto más seguro no prevé a los usuarios de especificar modificaciones incorrectas. Dichas modificaciones pueden causar serios daños al software o su ambiente. Por ejemplo modificar un esquema de base de datos sin suspender la ejecución de los objetos en la aplicación que lo usa, o el pasaje de código al protocolo metaobjeto que incluye errores semánticos. Además deben descubrirse errores potenciales dentro de las especificaciones de cambio antes de realizar el cambio. Cada cambio debe tener sólo efecto limitado en otras partes del software.

o Incremento en el número de componentes. Puede suceder que un sistema software reflexivo incluya más metaobjetos que componentes de nivel base. o Baja eficiencia. Los sistemas de software reflexivos son normalmente más lentos que los sistemas no-reflexivos. Esto es causado por la compleja relación entre el nivel base y el meta nivel. Siempre que el nivel base sea incapaz de decidir cómo continuar con el cómputo, pide ayuda al meta nivel. Esta capacidad reflexiva requiere procesamiento extra: recuperación de información, metaobjetos cambiantes, chequeo de consistencia, y la comunicación entre los dos niveles disminuye la performance global del sistema. o Escasa cambiabilidad. Aunque un patrón Reflection ayuda con el desarrollo de software cambiable, se soportan sólo cambios que puedan realizarse a través del protocolo del metaobjeto. Como resultado, no es posible integrar fácilmente todos los cambios imprevistos a una aplicación, por ejemplo cambios o extensiones al código del nivel base.

Ejemplo

CLOS (Common Lisp Object System) es un clásico ejemplo de un lenguaje de programación reflexiva. En CLOS las operaciones definidas por objetos son llamadas funciones genéricas y su procesamiento se refiere a invocaciones a funciones genéricas Una invocación a una función genérica es dividida en tres fases:

- El primer sistema determina los métodos que son aplicables a la invocación dada.

- Luego ordena los métodos aplicables en orden decreciente de precedencia.

- El sistema finalmente establece la ejecución de una lista de métodos aplicables. En CLOS más de un método puede ser ejecutado en respuesta a una invocación dada.

El proceso de invocaciones de funciones genéricas es definido en el protocolo del metaobjeto de CLOS. Básicamente, este ejecuta una cierta secuencia de funciones genéricas del meta nivel. A

través de CLOS, los usuarios del protocolo metaobjeto pueden variar la conducta de una aplicación modificando estas funciones genéricas o las funciones genéricas del metaobjeto llamados por ellos.

5. PATRONES DE DISEÑO Los patrones de diseño se han convertido en una técnica importante para el reuso del conocimiento de software. Cada patrón provee información sobre su diseño, describiendo las clases, métodos y relaciones que resuelven un problema de diseño en particular.

Los patrones han sido agrupados y organizados en catálogos cada uno dando diferentes clasificaciones y descripciones.

El proceso de construcción de aplicaciones utilizando patrones de diseño se reduce a que, cada vez que el diseñador encuentra un problema, busca en los catálogos un patrón que lo resuelva. Si existiera tal patrón, se deben identificar las clases, métodos y atributos de la aplicación que juegan el rol de aquellos prescritos por el patrón.

PATRONES DE CREACIÓN Estos patrones facilitan la creación de objetos en un sistema, debido a que la mayor parte de los sistemas Orientados a Objetos necesitan crear instancias de clases.

Los patrones de creación muestran la guía de cómo crear objetos. Las decisiones que se deben tomar al momento de la creación de los objetos normalmente serán resueltas dinámicamente decidiendo qué clases instanciar o qué objetos delegarán responsabilidades sobre otros objetos. Es decir, los patrones de creación abstraen el proceso de instanciación, ayudan a independizar a un sistema, de cómo sus objetos son creados. En general, tratan de ocultar las clases y métodos concretos de creación, de tal forma que al variar su implementación, no se vea afectado el resto del sistema.

Abstract Factory

Crea familias de objetos relacionados o dependientes sin necesidad de especificar su clase concretamente.

Aplicabilidad

Se usa el patrón Abstract Factory cuando el sistema a desarrollar debe: Ser independiente de cómo se crean, se componen y se representan sus productos. Debería ser configurado mediante una de las múltiples familias de productos posibles. Se diseña una familia de objetos relacionados que deben ser usados en conjunto. Proveer una librería de una clase de productos y es necesario revelar simplemente sus

interfaces, pero ocultar sus implementaciones.

Participantes

AbstractFactory: Declara una interfaz para operaciones que crean objetos de productos abstractos.

ConcreteFactory: Implementa las operaciones para crear objetos de productos concretos.

AbstractProduct: declara una interfaz para objetos de un tipo de producto

ConcreteProduct: Define un objeto de producto que va a ser creado por su correspondiente ConcreteFactory.

Cliente: usa solo interfaces declaradas.

Figura 5.1 - Patrón de Diseño Abstract Factory

Consecuencias

El patrón Abstract Factory tiene las siguientes ventajas:

o Aísla clases concretas: Ayuda a controlar las clases de objetos que crea una aplicación. Debido a que una fabrica encapsula la responsabilidad y la creación de objetos de productos, ella aísla a los clientes de la implementación de clases, permitiéndoles manipular instancias a través de sus interfaces abstractas.

o Facilita el cambio en familias de productos: La clase de una fábrica concreta aparece solo una vez en la aplicación, lo que facilita su cambio en una aplicación.

o Promueve la consistencia entre los productos: Cuando los objetos de productos en una familia son designados a trabajar juntos, es importante que una aplicación utilice objetos de solo una clase a la vez.

o Es difícil mantener nuevos tipos de productos: No es fácil extender una fábrica abstracta para que produzca nuevos tipos de productos, porque su interfaz fija el conjunto de productos que se pueden crear. Por lo cual agregar un nuevo producto requiere extender la interfaz de la fábrica, lo cual implica cambios en el código de la clase Abstract Factory y todas sus subclases.

Ejemplo

Un ejemplo de uso del patrón Abstract Factory es su uso en la creación de widgets (ventanas, scroll-bars, botones, etc.) ya que su forma de implementación es dependiente del sistema operativo subyacente. Se declara una fábrica abstracta Widget, la cual implica varias fabricas concretas de widgets adaptadas a cada situación en particular (diferentes aplicaciones utilizan widgets de forma diferente ) de las cuales el usuario no se da cuenta de su existencia.

Builder

Separa el proceso de construcción de un objeto complejo de su representación, para que el mismo proceso de construcción pueda crear diferentes representaciones.

Aplicabilidad

Se utiliza el patrón Builder cuando:

El algoritmo para crear objetos complejos debe ser independiente de las partes que constituyen el objeto y la forma en que son ensamblados.

La construcción de procesos debe permitir diferentes representaciones para el objeto que se construye.

Participantes

Builder: Especifica una interfaz abstracta para crear partes de un objeto de Producto.

ConcreteBuilder: Construye y ensambla partes del producto mediante la implementación del patrón Builder, define y controla la representación que crea, y además, provee una interfaz para consultar el producto.

Director: construye un objeto usando el patrón Builder

Producto: Representa al objeto complejo bajo construcción. ConcreteBuilder crea la representación interna del producto y define el proceso por el cual es ensamblado.

Figura 5.2 - Patrón de Diseño Builder

Consecuencias

Las consecuencias más importantes son:

o Permite variar la representación interna de un producto: El objeto Builder provee al director de una interfaz abstracta para construir el producto. La interfaz permite al Builder ocultar la representación y la estructura interna del producto, y la forma en que éste se ensambla. Como el producto se construye a través de una interfaz abstracta de debe definir un nuevo tipo de constructor para cambiar la representación interna de un producto.

o Aísla los códigos de construcción y representación: El patrón Builder agrega modularidad encapsulando la forma en que un objeto es construido y la forma en que es representado. Los clientes no necesitan conocer nada de la clase que define la estructura interna del producto, tal clase no aparece en la interfaz del Builder. Cada ConcreteBuilder contiene el código para crear y ensamblar un tipo de producto en particular. El código se escribe una vez, luego diferentes Directores pueden usarlos para construir distintos productos desde el mismo conjunto de partes.

o Mayor control en el proceso de construcción: El patrón Builder construye el producto paso a paso bajo el estricto control del Director. Solamente cuando se finaliza el producto, el director lo recupera del constructor. Esto brinda mayor control sobre el proceso de construcción y consecuentemente en la estructura interna del producto resultante.

Ejemplo

Las PIMs son bases de datos especiales. Una PIM incluirá normalmente una libreta de direcciones, un calendario para agendar las actividades y citas y una lista de tareas a realizar donde se listan a las mismas, las llamadas por realizar y cosas que queden por hacer.

Para lograr la gestión de citas pueden definir una clase llamada Cita para la información de cada evento, de tal forma que pueda registrar información como por ejemplo fechas de inicio y finalización de la misma, una descripción de la cita, un lugar de encuentro, asistentes a la cita, etc.

La información es introducida por un usuario cuando se crea la cita, por lo que se define un constructor que permite establecer el estado del nuevo objeto Cita.

Son necesarios diferentes tipos de información dependiendo del tipo de cita. Se puede necesitar una lista de asistentes, algunas pueden tener fecha de inicio y fin, otras una sola fecha. Cuando se consideran todas estas opciones la creación de un objeto Cita no es una tarea trivial. Es por ello que se delega la responsabilidad de la creación de citas a una clase especial, ConstructorCita, lo que simplificará mucho el código de la propia Cita. La clase ConstructorCita contiene métodos para crear las partes de Cita, y puede llamar a los métodos de ConstructorCita que son relevantes para cada tipo de cita. Además, la clase ConstructorCita puede asegurar que la información que recibe cuando se crea la cita es válida, ayudando así a que se cumplan las reglas de negocio. Si necesita crear subclases de Cita, puede crear otra clase constructor o heredar de la que ya existe. En

cualquier caso, esa tarea es más fácil que la alternativa de gestionar la inicialización de objetos por medio de constructores.

Prototype

El patrón Prototype (Prototipo), crea objetos nuevos copiándolos, clonando una instancia creada previamente.

Aplicabilidad

Se utiliza este patrón cuando un sistema debe ser independiente de cómo se crean, se componen y se representan sus productos, además cuando:

Las clases a instanciar son especificadas en tiempo de ejecución Para evitar la construcción de una jerarquía de la clase de fábricas que se asemeja a la

jerarquía de la clase de productos Cuando instancias de una clase pueden tener una de las combinaciones de diferentes estados. Cuando es necesario la creación de distintas variantes de un objeto. Entonces, el código que

utilizan esos objetos solicitará una copia del objeto que necesite, una copia significa otra instancia del objeto. El único requisito que debe cumplir el objeto es la posibilidad de clonarse.

Participantes

Prototype: Declara una interfaz para clonarse.

ConcretePrototype: Implementa una operación para clonarse.

Cliente: Crea un nuevo objeto por medio de una solicitud de clonación al prototype.

Figura 5.3 - Patrón de Diseño Prototype

Consecuencias

El patrón Prototype oculta las clases producto del cliente. Este patrón cuenta con las siguientes ventajas:

o Agregar y quitar productos en tiempo de ejecución: El patrón Prototype permite que se incorpore una clase producto en el sistema simplemente registrando una instancia de prototipo. Esto es más flexible que la forma en que lo brinda otro patrón de creación, debido a que el cliente puede agregar o quitar prototipos en tiempo de ejecución.

o Especificar nuevos objetos variando sus valores: En sistemas altamente dinámicos permite definir nuevos comportamientos a través de composición de objetos especificando valores para variables de un objeto sin definir nuevas clases. Se define una nueva clase de objetos clonando clases existentes y registrando las instancias como prototipos de objetos cliente. Un cliente puede variar su comportamiento delegando responsabilidades al prototipo. Este tipo de diseño le permite a los usuarios definir nuevas “clases” sin programar, en realidad, clonar un prototipo es similar a instanciar una clase. Este patrón puede disminuir el número de clases que necesita el sistema.

o Reducir la subclasificación: Este patrón permite clonar un prototipo en vez de solicitar a un método fábrica que cree un nuevo objeto, lo cual evita tener toda una jerarquía de clases Creadoras de objetos.

o Permite configurar una aplicación con clases dinámicamente: Una de las mayores ventajas de este patrón es permitir en tiempo de ejecución cargar dinámicamente las clases en una aplicación.

El patrón Prototype presenta la siguiente desventaja:

o Cada subclase de Prototype debe ser implementada por la operación Clone, tarea que puede resultar complicada cuando las clases ya existen y los objetos que estas incluyen no soportan ser copiados o tienen referencias circulares.

Ejemplo

Si nos referimos al ejemplo planteado en el patrón de diseño Builder, en el PIM tal vez se necesite copiar una dirección con el fin de que el usuario no tenga que introducir manualmente toda la información cuando crea un nuevo contacto. Una forma seria crear un nuevo objeto Dirección y luego copiar los valores apropiados del objeto Dirección existente. Esta solución tiene un problema: viola el principio de encapsulación de la orientación a objetos. Para alcanzar la solución se deberían hacer llamadas a los métodos para copiar la información de Dirección fuera de la clase Dirección. Esto significa que se hace más difícil mantener el código de la clase Dirección porque se extiende a lo largo de todo el proyecto. Además también dificulta la reutilización de la clase Dirección.

El código utilizado para realizar la copia realmente pertenece a la clase Dirección, entonces se podría definir un método copy() que produzca un duplicado del objeto Dirección con los mismos datos que el objeto original, el prototipo. Por lo tanto una llamada al método en un objeto

Dirección existente resuelve el problema de una forma mucho más fácil de mantener y más aceptable según las prácticas correctas de programación orientada a objetos.

PATRONES ESTRUCTURALES Detallan la manera en que se pueden relacionar, distintos tipos de objetos, para trabajar unos con otros y formar estructuras de mayor tamaño.

Adapter

El patrón Adapter se aplica para convertir una interfaz de una clase en otra, haciendo que una clase a la que le fuera imposible utilizar la primer interfase, haga uso de ella por medio de la segunda. Es decir, permite que éstas trabajen juntas, lo que de otra forma sería incompatible.

Aplicabilidad

El patrón Adapter se recomienda utilizar cuando:

Es necesario el uso de una clase existente, y su interfaz es distinta a la que se necesita. Se requiere crear una clase reusable que coopere con clases no relacionadas, es decir, con

clases que no necesariamente tienen interfaces compatibles.

Participantes

Target : define la interfaz específica del dominio que usa Client.

Client: participa en la formación de objetos para la interfaz Target.

Adaptee (Adaptado): especifica una interfaz existente que define los métodos que necesitan ser adaptados.

Adapter(Adaptador): adapta la interfaz de Adaptee a la interfaz Target.

Figura 5.4 - Patrón de Diseño Adapter

Consecuencias

El Patrón Adapter presenta las siguientes ventajas y desventajas:

o Reutilización de Código: El patrón Adapter ofrece una opción para la reutilización del código, permitiendo la interacción de dos o más objetos que supuestamente son incompatibles.

o Transferencia de argumentos: Mediante el objeto Adapter se crean objetos apropiados cuando no hay una correspondencia directa entre el Target y el Adaptee, o encapsula un objeto para que pueda ser utilizado por el Adaptee.

Existen otras consideraciones a tener en cuenta en el momento de utilizar el patrón Adapter:

o ¿Cuantas veces el Adapter realiza adaptaciones? Varía en la cantidad de trabajo que hace Adapter para adaptar Adaptee a la interfaz Target. Existe una variedad de tareas que se puede requerir que Adapter lleve a cabo, desde una simple conversión (cambiar los nombres de las operaciones) hasta soportar un conjunto de operaciones totalmente diferentes. El trabajo depende de que tan similar o diferentes sean las interfaces de Target con las de Adaptee.

o Adaptadores Pluggables: Una clase es más reutilizable cuando se minimiza las asunciones que otras clases deben hacer para utilizarla. Por medio de la construcción de la adaptación de una interfaz en una clase, se elimina la posibilidad de que otras clases vean la misma interfaz. Es decir, al adaptar la interfaz de una clase permite incorporar una clase propia en un sistema existente que puede esperar diferentes interfaces para dicha clase.

Ejemplo

Un ejemplo del mundo real en el que podemos aplicar este patrón es en un libro de frases de idiomas extranjeros. Este libro traduce expresiones comunes (mensajes) de un idioma a otro, permitiendo que dos elementos incompatibles se comuniquen entre sí.

Bridge

Mediante el patrón Bridge es posible desacoplar una abstracción de su implementación, de forma que ambas puedan modificarse de manera independiente sin necesidad de alterar por ello la otra.

Aplicabilidad:

Se puede utilizar el patrón Bridge cuando:

Se desea evitar una vinculación permanente entre la abstracción y su implementación. Este puede ser el caso en que la implementación debe ser seleccionada o modificada en tiempo de ejecución.

Las abstracciones y sus implementaciones deben ser extensibles a través de subclases. En este caso, el patrón Bridge permite combinar diferentes abstracciones e implementaciones y extenderlas en forma independiente.

Los clientes no deben tener que recompilar el código cuando se lleven a cabo modificaciones en la implementación de una abstracción.

Se desea ocultar completamente la implementación de una abstracción a los clientes. Se necesita compartir una implementación entre varios objetos, y este hecho debe ocultarse a

los clientes.

Participantes:

Abstraction: define una interfaz abstracta. Mantiene una referencia a un objeto de tipo Implementor.

RefinedAbstraction: extiende la interfaz definida por Abstraction.

Implementor: define la interfaz para la implementación de clases. Esta interfaz no es necesario que corresponda exactamente con la interfaz de Abstraction; ya que por lo general, la interfaz Implementor provee sólo operaciones primitivas, y Abstraction define operaciones de alto nivel basadas en esas primitivas.

ConcreteImplementor: implementa y define la implementación de la interfaz de Implementor.

Figura 5.5 - Patrón de Diseño Bridge

Consecuencias

o Separa interfaz e implementación: Una implementación de una abstracción no esta permanentemente confinada a una interfase, ya que puede ser configurada en tiempo de ejecución. Además, un objeto tiene la posibilidad de cambiar su implementación en tiempo de ejecución. El hecho de desacoplar Abstraction e Implementor hace que se elimine en tiempo de compilación las dependencias sobre una clase de implementación.

o Mejora la extensibilidad: se puede extender en forma independiente las jerarquías de Abstraction e Implementor.

o Oculta a los clientes los detalles de implementación: Se puede proteger a los clientes de los detalles de implementación.

Ejemplo

Ejemplos de la aplicación de este patrón son los sistemas de interfaz gráfica que deben ser portables entre plataformas, estos sistemas necesitan que la implementación subyacente sea aplicada cuando la aplicación se inicia en un sistema operativo diferente.

Composite

El patrón Composite admite construir objetos complejos por medio de la composición recursiva de objetos similares u otros más simples en una estructura en forma de árbol. Además permite que dichos objetos sean tratados de manera semejante, sin hacer distinciones entre ellos.

Esto reduce el tratamiento de los objetos creados, debido a que como todos ellos poseen una interfaz común los trata de igual manera a todos.

Aplicabilidad

Se recomienda utilizar el patrón Composite cuando:

Es necesario representar la jerarquía completa de objetos. Se necesita que el cliente no note la diferencia entre una composición de objetos y un objeto

individual, de esta forma el cliente tratará a todos los objetos en la composición de la estructura uniformemente.

Participantes

Component: Declara la interfaz para objetos en la composición. Implementa comportamientos por defecto para la interfaz común a todas las clases. Declara una interfaz para acceder y administrar sus componentes hijos.

Leaf: Representa las hojas en el árbol de la composición. Define el comportamiento para objetos primitivos.

Composite: Define el comportamiento para componentes que tienen hijos. Almacena los componentes hijos.

Client: opera con objetos en la composición a través de la interfaz Component.

Figura 5.6 - Patrón de Diseño Composite

Consecuencias

El patrón Composite tiene las siguientes ventajas:

o Define jerarquías de clases formada por objetos primitivos. Los objetos primitivos pueden estar compuestos dentro de objetos complejos los cuales a su vez pueden ser compuestos y así recursivamente.

o Clientes más simples: Los clientes pueden tratar en forma uniforme estructuras compuestas y objetos individuales. Para los clientes es transparente si están tratando con una hoja o un componente compuesto. Esto simplifica el código del cliente.

o Es sencillo agregar componentes nuevos: Nuevos componentes Composite o subclases Leaf trabajan automáticamente con estructuras existentes y códigos de clientes. Los Clients no deben ser cambiados por nuevas clases Component.

El patrón Composite presenta las siguientes desventajas:

o Puede hacer el diseño demasiado general: Al agregar nuevos componentes de forma fácil, dificulta restringir los componentes de una composición, especialmente cuando se requiere que una composición tenga solo ciertos componentes.

Ejemplo

Un ejemplo clásico de aplicación de este patrón se observa en las aplicaciones gráficas, donde el usuario puede crear dibujos complejos a partir de dibujos sencillos y básicos (Hojas), pero a su vez puede manipular conjuntamente sólo una parte del dibujo, sin hacer distinción si es una figura simple o compuesta.

Proxy

El patrón Proxy se emplea como intermediario para acceder a un objeto, controlando el acceso a él.

Aplicabilidad

El patrón Proxy es aplicable cuando se necesita referenciar a un objeto en forma más refinada y versátil que con un simple puntero. En las siguientes situaciones es conveniente el uso del patrón Proxy:

Un proxy remoto provee una representación local de un objeto ubicado en un espacio de dirección diferente.

Un proxy virtual crea objetos de gran tamaño bajo demanda. Un proxy de protección controla el acceso al objeto original, controlando quien accede a cada

método y otorgando los permisos basándose en el invocador. Una referencia elegante lleva a cabo acciones extras cuando se acceden a objetos

referenciados. El uso normal incluye: o Contar el número de referencias al objeto para que pueda ser liberado automáticamente

cuando no haya más referencias a él. o Cargar un objeto persistente a memoria cuando es referenciado por primera vez.

o Chequear que el objeto este resguardado antes de ser accedido para asegurarse que ningún otro objeto lo pueda modificar.

Participantes

Proxy: Mantiene una referencia que le permite acceder al objeto real. Mantiene una interfaz idéntica a Subject lo que permite que un Proxy sustituya al objeto real. Controla al acceso al objeto real y puede ser responsable de crearlo o eliminarlo. Según el tipo de proxy puede:

Proxy remoto: Codificar peticiones y sus argumentos para enviar al RealSubject en otro espacio de dirección

Proxy virtual: Guarda información adicional del RealSubject Proxy de protección: Comprueba los permisos de acceso al RealSubject.

Subject: Define la interfaz común a Proxy y RealSubject

RealSubject: Define el objeto real que representa el Proxy

Figura 5.7 - Patrón de Diseño Proxy

Consecuencias

El patrón Proxy introduce un nivel de indirección cuando se accede a un objeto.

La indirección adicional tiene varios usos y ventajas, dependiendo de la clase de proxy:

Proxy Remoto: oculta al cliente el hecho que un objeto reside en un espacio de memoria distinto.

Proxy Virtual: tiene un sustituto con el que interactúa y no crea el producto real hasta que realmente lo necesita, y optimiza el hecho de crear un objeto bajo demanda.

Proxy de Protección: realiza tareas de control cuando se accede a un objeto.

Ejemplo

Se desea resguardar una lista de usuarios registrados, pero no se sabe exactamente donde resguardarla. La decisión consiste que, en base al valor de un checkbox (en el que el usuario elige si está conectado a internet o no), se debe enviar los datos al servidor (en formato XML) o guardarlos en disco.

Esta es una tarea para realizarse bajo el patrón Proxy, ya que se quiere aislar el proceso de guardar los datos del resto del programa, simplemente se desea pedir que se guarden dichos datos pero no cómo o dónde se debe guardar.

Eso es lo que realiza este patrón. El proxy guarda una referencia a un objeto, que es quien realmente se encargará de ejecutar la acción. Pero es el proxy el que crea ese objeto, por lo que puede crear una instancia de una u otra clase dependiendo de ciertas condiciones en tiempo de ejecución

PATRONES DE COMPORTAMIENTOLos patrones de comportamiento describen la forma de como organizar, administrar, y combinar conductas y responsabilidades de objetos, centrándose en la comunicación entre ellos. Frecuentemente, describen como los distintos elementos colaboran entre si para conseguir un objetivo.

Template Method

El patrón Template Method proporciona un método abstracto que permite que las subclases redefinan partes del método sin reescribirlo completamente manteniendo su estructura inicial.

Aplicabilidad:

Se utiliza el patrón Template Method para

Proporcionar un esqueleto para un método permitiendo a las subclases redefinir partes específicas del mismo.

Centralizar partes de un método que se definen en todos los subtipos de una clase, pero que siempre tiene una pequeña diferencia en cada subclase.

Controlar las operaciones que se necesitan redefinir en las subclases.

Participantes:

AbstractClass: Define operaciones primitivas abstractas que subclases concretas definen para implementar pasos de un algoritmo. Implementa un método template() definiendo el esqueleto de un algoritmo. Este método llama tanto a operaciones primitivas como a operaciones definidas en AbstractClass.

ConcreteClass: Es una subclase de AbstractClass e implementa los métodos abstractos definidos.

Figura 5.8 - Patrón de Diseño Template Method

Consecuencias

El patrón Template Method cuenta con una ventaja muy importante y es que facilita la reutilización de código, evitando que el código se duplique en muchas subclases.

Este patrón tiene como desventaja que si el método template() llama a demasiados métodos abstractos, se cansará pronto de utilizar AbstractClass como superclase. Es recomendable tener un número limitado de métodos abstractos.

Ejemplo

Los métodos de plantilla se utilizan sobre todo en frameworks para, a la vez que se garantiza que las operaciones se ejecutan en el orden correcto, otorgar flexibilidad a los usuarios del framework permitiendo ejecutar su propio código en determinados puntos, simplemente redefiniendo estos métodos (abstractos en la clase original).

Observer

El patrón Observer define una dependencia del tipo uno-a-muchos entre objetos, de manera que cuando uno de los objetos cambia su estado, el observer se encarga de notificar este cambio a todos los otros objetos dependientes.

Aplicabilidad:

Se puede utilizar el patrón Observer cuando se presentan las siguientes situaciones:

Una abstracción tiene dos aspectos, uno dependiente del otro. Encapsulándolos en objetos separados se permite variarlos y reusarlos de forma independiente.

Cuando un cambio en un objeto implica cambiar otros y no se conoce de antemano cuantos objetos deben actualizarse

Cuando un objeto debe ser capaz de hacer notificaciones a otros sin hacer suposiciones de quiénes son, buscando un bajo acoplamiento.

Participantes:

Subject: Proporciona métodos para suscribir y borrar Observers. Conoce sus Observers.

Observer: Define la interfaz para objetos que deben ser notificados.

ConcreteSubject: Almacena el estado que es de interés para los objetos ConcreteObserver y envía notificaciones a sus Observers cuando cambia su estado.

ConcreteObserver: Mantiene una referencia a un objeto ConcreteSubject. Almacena el estado que debe ser consistente con el del Subject. Implementa la interfaz de actualización para mantener su estado consistente con el del Subject.

Figura 5.9 - Patrón de Diseño Observer

Consecuencias

Ventajas:

o Acoplamiento mínimo entre Subject y Oberver: Todo lo que sabe un subject es que tiene una lista de observers que responden a la interfaz observer. El subject no conoce ninguna clase observer concreta.

o Soporte para comunicación tipo broadcast: La notificación se extiende a todos los objetos de la lista. Se añaden y quitan observadores en cualquier momento.

Desventajas:

o Actualizaciones Costosas: Un observer no conoce cuántos observers más existen y por lo tanto, no conoce el costo de enviar un “cambio” al subject. Podría producirse una actualización en cascada.

Ejemplo

Un ejemplo clásico de la aplicación del patrón Observer se da cuando tenemos una aplicación la cual consta de distintas vistas, y dependiendo de cada una de ellas los datos son actualizados de distinta forma y en distintos momentos.

CommandEste patrón permite solicitar una operación a un objeto sin conocer realmente el contenido de esta operación, ni el receptor real de la misma. Para ello se encapsula la petición como un objeto, facilitando la parametrización de los métodos.

Al encapsular un mensaje como un objeto, permite gestionar colas o registros de mensajes, deshacer operaciones y restaurar el estado a partir de un momento dado. Ofrece una interfaz común que permite invocar las acciones de forma uniforme y extender el sistema con nuevas acciones de forma más simple.

Este patrón presenta una forma sencilla y versátil de implementar un sistema basado en comandos facilitando su uso y ampliación.

Aplicabilidad:

Se puede utilizar el patrón Command cuando se necesita:

Facilitar la parametrización de las acciones a realizar Independizar el momento de petición del de ejecución Implementar CallBacks, especificando que órdenes se necesitan ejecutar en ciertas situaciones

bajo otras órdenes. Es decir, un parámetro de una orden puede ser otra orden a ejecutar. o Dar soporte para deshacer comandos, procesos de identificación y/o transacciones o Desarrollar sistemas utilizando órdenes de alto nivel que se construyen con

operaciones sencillas (primitivas).

Participantes:

Command: Declara una interfaz para la ejecución de una operación.

ConcreteCommand: Define una enlace entre el objeto Receiver y una acción. Implementa Execute() por invocación a la operación correspondiente en el Receiver.

Client: Crea un objeto ConcreteCommand y setea su receptor

Invoker : Solicita al command que envíe la respuesta a su solicitud

Receiver: Conoce la forma en que las operaciones son asociadas con el envío de una solicitud. Una clase puede servir como un Receiver.

Figura 5.10 - Patrón de Diseño Command

Consecuencias

o Independiza el objeto que invoca una operación de un objeto que conoce como implementarla.

o Los Command son objetos de primera clase, pueden ser manipulados y extendidos como cualquier otro objeto.

o Se facilita la ampliación del conjunto de comandos ya que las clases existentes no cambian.

Ejemplo

Este patrón permite estructurar un sistema en torno a operaciones de alto nivel que se implementan en término de operaciones, por ejemplo, un sistema transaccional. Permite implementar un mecanismo de rehacer, deshacer permitiendo llevar un registro de las operaciones que sí pueden volver a ser aplicadas, por ejemplo, en caso de una caída del sistema.

State

El patrón State permite que un objeto cambie en tiempo de ejecución su comportamiento cuando cambia su estado interno.

Aplicabilidad:

Se puede utilizar el patrón State en cualquiera de los siguientes casos:

El comportamiento de un objeto depende de su estado y este cambia con mucha frecuencia. Los métodos tienen largas y múltiples sentencias condicionales que dependen del estado del

objeto. Estos estados generalmente son representados por constantes enumeradas y largas sentencias “switch/case” dentro de los métodos. El patrón State ubica cada rama del condicional en clases separadas.

Participantes:

Context: Define la interfaz que utilizan los clientes y mantiene una referencia al estado actual del objeto.

State: Define una interfaz para encapsular el comportamiento asociado a un estado particular del objeto.

ConcreteSate: Cada subclase implementa un comportamiento asociado con un estado del objeto.

Figura 5.11 - Patrón de Diseño State

Consecuencias

El uso del patrón State presenta las siguientes ventajas e inconvenientes:

o Comportamiento de las particiones de estados basadas en el estado: Esto da una visión más clara del comportamiento. Cuando un objeto es un estado específico, mira en la subclase State correspondiente y todo el posible comportamiento de ese estado se incluye en esa clase.

o Localiza comportamiento para un estado específico y particiona el comportamiento para diferentes estados: El patrón State ubica todos los comportamientos asociados a un estado particular en un objeto. Como todos los códigos específicos a un estado conviven en una subclase de State, agregar nuevos estados se transforma en una tarea muy sencilla, solo es necesario definir nuevas subclases.

o Las transiciones de estados son explicitas: Cuando un objeto define su estado actual mediante la asignación de un valor a una variable, su transición de estado no tiene una representación explícita. Introducir objetos separados por diferentes estados hace que la transición se torne más explícita, facilitando el reconocimiento de un cambio entre estados.

o Utiliza un gran número de clases: Si bien puede verse como una desventaja, no lo es, debido a que presenta una visión mucho más clara que el uso de largas sentencias switch en los métodos.

Ejemplo

Un ejemplo del uso del patrón State es considerar clases TCPConnection que representan el estado de la conexión de una red. Un objecto TCPConnection puede estar en uno de los varios estados posibles de la conexión: establecida, escuchando, cerrada. Cuando un objeto

TCPCOnnection recibe solicitudes desde otros objetos, este responderá de forma diferente dependiendo del estado en que se encuentre.

Strategy

El patrón Strategy define una familia de algoritmos, encapsula cada uno y los hace intercambiables, permitiendo que el algoritmo varíe independientemente del cliente que haga uso de él.

Aplicabilidad:

Se puede utilizar el patrón Strategy cuando:

Se quiera ofrecer la posibilidad de configurar una clase con una gama de comportamientos disponibles.

Se necesiten diferentes variantes de un algoritmo. Un algoritmo use datos que el cliente no tenga por qué conocer. Una clase defina varios comportamientos y estos aparezcan en forma condicional en sus

operaciones.

Participantes:

Strategy: Declara la interfaz común para todos los algoritmos soportados.

ConcreteStrategy: Implementa un algoritmo usando la interfaz de Strategy.

Context: Mantiene una referencia a un objeto Strategy, puede definir una interfaz para permitir a Strategy acceder a sus datos. Se configura con un objeto ConcreteStrategy.

Figura 5.12 - Patrón de Diseño Strategy

Consecuencias

Ventajas

o Familias de algoritmos relacionados: Mediante herencia a partir de clases Strategy este patrón define una familia de algoritmos o comportamientos para contextos reusables. La herencia puede ayudar a factorizar la funcionalidad común de los algoritmos.

o Elimina las sentencias condicionales.

o Strategy puede proveer diferentes implementaciones del mismo comportamiento.

Desventajas

o Puede producirse sobrecarga de comunicación en el paso de parámetros entre Context y Strategy.

o Puede haber parámetros muy costosos de crear y NO usados por estrategias muy simples. o Incrementan el número de objetos en una aplicación.

Ejemplo

Un ejemplo básico y sencillo para observar el uso de este patrón consiste en la implementación de un algoritmo de búsqueda determinado. De esta forma tenemos una interfaz (una función que recibe una lista y la ordena) pero con distintas formas de realizar la ordenación, diversos algoritmos, ordenación por burbuja, merge sort, quick sort, etc.

Null ObjectEl patrón Null Object provee un objeto como un substituto por la falta de un objeto de un tipo dado. El Objeto Nulo no realiza nada aunque esto no significa que no sea inteligente. Este oculta los detalles de sus colaboradores.

A veces una clase requiere de un colaborador pero no necesita que este haga algo. Sin embargo, la clase desea tratar a un colaborador que no hace nada de la misma manera que trata a uno que realmente proporciona conducta.

Aplicabilidad:

Se puede utilizar el patrón Null Object cuando:

un objeto requiere de un colaborador. El patrón Null Object no introduce esta colaboración, hace uso de una colaboración que ya existe.

algunos casos del colaborador que no deben hacer nada. cuando necesita abstraer el manejo de objetos nulos fuera del cliente.

Participantes:

Client: requiere a un colaborador.

AbstractObject: declara la interfase para el colaborador de Cliente e implementa la conducta por defecto para la interfase común a todas las clases.

RealObject: define una subclase concreta de AbstractObject cuyas instancias proveen un comportamiento útil que espera Client

NullObject: proporciona una interfase idéntica a AbstractObject para que un objeto nulo pueda ser sustituido para un objeto real. Implementa su interfase pero no hace nada. El significado de “no hacer nada” depende de qué clase de conducta Client esta esperando. Cuando hay más de una manera de no hacer nada, puede necesitarse más de una clase NullObject.

Figura 5.13 - Patrón de Diseño Null Object

Consecuencias

Ventajas

o Define jerarquías de la clase que consisten en objetos reales y en objetos nulos. Pueden usarse los objetos nulos en lugar de los objetos reales cuando se espera que el objeto no haga nada. Siempre que el código del cliente espere un objeto real, también puede tomar un objeto nulo.

o Hace el código del cliente simple. Los clientes pueden tratar a los colaboradores reales y los colaboradores nulos uniformemente. Los clientes normalmente no saben si ellos están tratando con un colaborador real o uno. Esto simplifica el código del cliente, porque evita tener que escribir código de comprobación, ya que se ocupa el colaborador nulo especialmente.

o Encapsula la tarea de “no hacer nada” en el código del objeto nulo. o Hacer el código de “no hacer nada” en el objeto nulo facilita su reuso . Múltiples clientes que

tienen la necesidad de que sus colaboradores no hagan nada querrán que la tarea de “no hacer nada” lo hagan todos de la misma forma. Si el “no hacer nada” necesita ser modificado, el código puede cambiarse en un lugar. Luego de esto, todos los clientes continuarán usando el mismo comportamiento actualizado de no hacer nada.

Desventajas

o La conducta de no hacer nada hace difícil distribuir o mezclar el comportamiento real de varias colaboraciones de objetos. La misma conducta de no hacer nada no puede agregarse fácilmente a la conducta de varias clases a menos que esas clases deleguen todo el comportamiento a una clase que puede ser una clase del objeto nulo.

o Puede ser necesario la creación de una nueva clase NullObject para cada nueva clase AbstractObject.

o Puede ser dificultoso la implementación si varios clientes no están de acuerdo de cómo el objeto nulo no debe hacer nada o cuando su interfaz a AbstractObject no se encuentra bien definida.

o El Objeto Nulo siempre actúa como un objeto que no hace nada y no se transforma en un Objeto Real.

Ejemplo

Considere por ejemplo una simple pantalla que despliega pelotas que se mueven sobre la pantalla y tienen efectos de color especiales. Esto se logra fácilmente creando una clase Pelota para representar las pelotas y usando un patrón Startegy para controlar el movimiento de la pelota y otro patrón Strategy para controlar el color de la pelota. Sería entonces trivial escribir las estrategias para tipos diferentes de movimiento y efectos de color y crear pelotas con cualquier combinación de movimiento y color. Sin embargo, si comenzamos por crear las estrategias más simples posiblemente se asegure todo el trabajo.

Ahora, la estrategia más simple no sería ninguna estrategia. Ése es no hacer nada, no mueve y ni cambia el color. Sin embargo, el patrón Strategy exige a la pelota tener objetos que llevan a cabo las interfaces de estrategia. Aquí es donde el patrón Null Object es útil. Simplemente se realiza un NullMovementStrategy que no mueve la pelota y un NullColorStrategy que no cambian el color de la pelota. Probablemente los métodos en estas clases no hacen "nada".

Type Object

Desacopla instancias de sus clases para que estas clases puedan ser implementadas como instancias de una clase. El patrón Type Object permite crear las nuevas "clases" dinámicamente en tiempo de ejecución, porque éstas son instancias, y también permite al sistema crear instancias de esas instancias de tipo clase.

Algunas veces una clase requiere no sólo un número indeterminado de instancias sino también una cantidad desconocida de subclases. Aunque un objeto del sistema pueda crear nuevas instancias bajo demanda, usualmente no puede crear nuevas clases sin una recompilación. Un diseño en el cual una clase tiene un número desconocido de subclases puede convertirse en una en la cual la clase tiene un número desconocido de instancias

Arquitectura de Software: Estilos y Patrones Adriana Almeira – Vanina Perez Cavenago Pag 73

Aplicabilidad

Se recomienda aplicar el patrón de diseño Type Object cuando:

Las instancias de una clase necesitan estar agrupadas para implementar los atributos y/o comportamiento común.

La clase necesita una subclase por cada grupo para implementar los atributos y/o comportamientos del grupo.

La clase requiere un gran número de subclases y/o la variedad total de subclases que quizás se requieren y se desconoce.

Se necesita poder crear nuevas grupos en tiempo de ejecución que quizás no se predijeron durante el diseño.

Se necesita poder cambiar una subclase de un objeto después de que se instanció sin tener que mutar a una nueva clase.

Se necesita jerarquizar los grupos recursivamente de modo que ese grupo sea a su vez un ítem de otro grupo.

Participantes

El patrón Type Object tiene dos clases concretas, una que representa objetos y otra que representa sus tipos. Cada objeto tiene un puntero su tipo correspondiente.

TypeClass: Es la clase de TypeObject, tiene una instancia separada para cada tipo de Object.

TypeObject: Es una instancia de TypeClass. Representa un tipo de Object. Establece todas las propiedades de un Object que son las mismas de todos los Objects del mismo tipo.

Class: Es la clase de Object. Representa instancias de TypeClass.

Object: Es una instancia de Class. Representa un único ítem que tiene un único contexto. Establece todas las propiedades de ése ítem que puede diferir entre ítems del mismo tipo. Tiene un TypeObject asociado que describe su tipo. Delega propiedades definidas por su tipo a su TypeObject.

Figura 5.14 - Estructura del patrón de diseño Type Object

Consecuencias

Las ventajas de un patrón TypeObject son:

o Crea clases en tiempo de ejecución: El patrón permite crear nuevas clases dinámicamente. Estas nuevas clases no son clases, sino instancias llamadas TypeObjects que son creadas por TypeClass de igual manera que cualquier instancia creada por su clase.

o Evita la explosión de subclases: El sistema no necesita un gran número de subclases para representar diferentes tipos de Objects. En vez de un gran número de clases, el sistema puede usar un TypeClass y varios TypeObjects.

o Oculta la separación de instancias y tipos: Un Object cliente no necesita estar consciente de la separación entre Object y TypeObject. El cliente realiza solicitudes de Object, y el Object decide que requerimiento envía al TypeObject. Los clientes que conocen al TypeObject pueden colaborar con ellos directamente sin ir a través de los Objects.

o Cambio dinámico de tipo: El patrón permite que el Object cambie dinámicamente su TypeObject, dando el mismo efecto que si sufriera un cambio de clase. Esto es mucho más simple que mutar un objeto a una nueva clase.

o Subclases independientes: TypeClass y Class pueden tener sus subclases independientemente.

o Múltiples tipos de objetos: El patrón permite que un Object tenga múltiples TypeObjects donde cada uno define alguna parte del tipo del Object. El Object debe decidir cual tipo de comportamiento delegan a cual TypeObject.

Las desventajas de la utilización del patrón TypeObject son:

o Diseño complejo: El patrón divide un objeto lógico en dos clases. Su relación, una cosa y su tipo, es difícil de entender. Es difícil reconocer o explicar la relación entre un TypeObject y un Object. Esta confusión va en decremento de la simplicidad y la mantenibilidad.

o Implementación compleja: El patrón saca diferentes implementaciones de las subclases y las coloca en diferentes estados de las instancias de TypeObject. Considerando que cada subclase podría implementar un método en forma diferente, el TypeClass puede implementar el método de una única forma y cada estado del TypeObject debe hacer que la instancia se comporte en forma diferente.

o Manejador de referencia: Cada objeto debe guardar una referencia a su TypeObject. De igual manera que un objeto conoce cual es su clase, un Object conoce cual es su TypeObject. Pero considerando que el sistema objeto o lenguaje automáticamente establece y mantiene la relación clase-instancia, en este caso debe ser la aplicación por si misma quien establece y mantiene la relación TypeObject-Object.

Ejemplo

Supongamos que nos interesa modelar unidades de medida. Para plantear la relación entre la unidad y su tipo aplicamos el Patrón Type Object que permite que varias instancias de una clase, en este caso Unidad, sean agrupadas de acuerdo con comunes atributos y/o comportamiento, evitando una explosión de numerosas subclases. Consta de dos clases: una que representa los objetos, Unidad (Class), y otra que representa sus tipos, TipoUnidad (TypeClass).

Decorador

El patrón de diseño Decorador proporciona una forma flexible de incorporar o eliminar funcionalidad de un componente dinámicamente sin modificar su aspecto o su funcionalidad.

Aplicabilidad

Es útil hacer uso del patrón Decorator cuando:

Se desea realizar cambios en forma dinámica de forma transparente a los usuarios, sin las restricciones propias de la creación de subclases.

Sea posible introducir o quitar responsabilidades de los componentes en tiempo de ejecución. Sea imposible utilizar herencia debido a que daría lugar a la aparición de multitud de subclases

para poder dar soporte a todas las combinaciones posibles.

Participantes

Para implementar el patrón Decorador, son necesarios los siguientes participantes:

Component: Componente que contiene el comportamiento genérico. Define la interfaz de los objetos a los que se les pueden añadir responsabilidades de forma dinámica.

Decorador: Puede ser una clase o una interfaz. Define los comportamientos estándar de todos los decoradores, también proporciona soporte para contener otros decoradores, es decir, mantiene una referencia a un objeto Component que puede ser un objeto ConcreteComponent u otro Decorador.

ConcreteDecorator: Añade responsabilidades al objeto Component al que hace referencia.

Figura 5.15 - Estructura del patrón de diseño Decorador

Consecuencias

El patrón de diseño Decorador cuenta con las siguientes ventajas:

o Más flexible: Ofrece la oportunidad de ajustar y aumentar fácilmente el comportamiento de un objeto en tiempo de ejecución.

o Evita que las clases altas de la jerarquía estén demasiado cargadas de funcionalidad: Es mucho más sencilla la tarea relacionada con la propagación, debido a que sólo se deberá escribir una serie de clases cada una de las cuales contendrá una parte específica de la funcionalidad. En otro caso, habría que programar todos los comportamientos en el propio componente.

o Nuevas responsabilidades: Permite que la adición de nuevas responsabilidades (nuevas clases de Decorators) independiente de las clases los Objetos que ellas extienden.

Hay que tener en cuenta también las siguientes desventajas:

o Un Decorator y su Component no son idénticos: Desde el punto de vista de la identidad de los objetos, un DecoratorComponent no es idéntico al Component. Por esto no se puede confiar en la identidad de los objetos cuando se usan Decorators.

o Propagación de objetos: El patrón Decorator hace que haya muchos objetos pequeños que son muy parecidos.

Ejemplo

Supongamos que tenemos una clase existente Ventana y queremos añadirle funcionalidad para que muestre un borde alrededor. El patrón Decorator ayuda a resolver esta situación de una manera sencilla y extensible.

Se crea a partir de la clase Ventana la subclase abstracta VentanaDecorator y, heredando de ella, BordeDecorator y BotonDeAyudaDecorator. VentanaDecorator encapsula el comportamiento de Ventana y utiliza composición recursiva para que sea posible añadir tantas "capas" de Decorators como se desee.

Podemos crear tantos Decorators como queramos heredando de VentanaDecorator.