39
Patrones de Diseño Diseño de Software

Patrones de diseño

Embed Size (px)

Citation preview

Patrones de DiseñoDiseño de Software

Integrantes

Manuel Alejandro Damián Edsel Barbosa González José Luis Garza Gallegos Kevin Roberto Gómez Peralta Amayelli Itzel Silva Contreras

Patron Observer

Define una dependencia de uno a muchos entre objetos, de forma que cuando un objeto cambia de estado se notifica y actualizan automáticamente todos los objetos.

El objeto observado notifica a sus observadores cada vez que ocurre un cambio. Después de ser informado de un cambio en el objeto observado, cada observador concreto puede pedirle la información que necesita para reconciliar su estado con el de aquél.

Existen de 3 tipos: Creación, Estructurales y de Comportamiento. Los patrones de comportamiento describen no solamente estructuras

de relación entre objetos o clases sino también esquemas de comunicación entre ellos y se pueden clasificar en función de que trabajen con clases (Método Plantilla) u objetos (Cadena de Responsabilidad, Comando, Iterador, Recuerdo, Observador, Estado, Estrategia, Visitante).

Los observadores están obligados a implementar unos métodos determinados mediante los cuales el Sujeto es capaz de notificar a sus observadores suscritos los cambios que sufre para que todos ellos tengan la oportunidad de refrescar el contenido representado.

Nombre del Patrón: Observer (Observador, Publishe-Suscriptor).

Este patrón es un patrón de comportamiento. Su intención es proporcionar a los componentes una forma flexible de enviar mensajes de difusión a los receptores interesados.

El patrón Observer es la clave del patrón de arquitectura Modelo Vista Controlador (MVC).

Motivación: Muchas veces un efecto lateral de partir un sistema en una colección de objetos relacionados es que necesitamos mantener la consistencia entre objetos relacionados.

Diagrama de clases del patrón

Participantes: Subject: Conoce a sus observadores, Proporciona una

Interfaz para que se suscriban los objetos Observer. Observer: Define una interfaz para actualizar los

objetos que deben ser notificados de cambios en el objeto Subject.

ConcreteSubject: Guarda el estado de interés para los objetos ConcreteObserver, Envía una notificación a sus observadores cuando cambia su estado.

ConcreteObserver: Mantiene una referencia a un objeto ConcreteSubject, Guarda el estado que debería permanecer sincronizado con el objeto observado, Implementa la interfaz Observer para mantener su estado consistente con el objeto observado.

Consecuencias

Las consecuencias de aplicar este patrón pueden ser tanto beneficiosas como pueden perjudicar algunos aspectos. Por una parte abstrae el acoplamiento entre el sujeto y el observador, lo cual es beneficioso ya que conseguimos una mayor independencia y además el sujeto no necesita especificar los observadores afectados por un cambio. Por otro lado, con el uso de este patrón ocurre que vamos a desconocer las consecuencias de una actualización, lo cual, dependiendo del problema, puede afectarnos en mayor o menor medida (por ejemplo, al rendimiento).

Problema y Solución

Opciones o problemas que se pueden presentar a la hora de implementar este patrón:

Opción 1º: Para evitar que el observador concreto tenga una

asociación con el sujeto concreto se puede hacer que sea una relación bidireccional, evitando así asociaciones concretas, el problema es que dejaría de ser una interfaz; pero puede producir problemas si el lenguaje de programación no soporta la herencia múltiple. Se podría eliminar la bidireccionalidad de la asociación pasando el sujeto como parámetro al método actualizar y ya no se tendría que referenciar el objeto observado.

Opción 2º:

Si hay muchos sujetos sin observador, la estructura de los observadores está desaprovechada, para solucionarlo podemos tener un intermediario que centralice el almacenamiento de la asociación de cada sujeto con sus observadores. Para esta solución creamos ese gestor de observadores usando el patrón Singleton(Instancia única), ya que nos proporciona una única referencia y no una por cada sujeto. El gestor aunque mejora el aprovechamiento del espacio, hace que se reduzca el rendimiento y se pierde eficiencia en el método notificar.

Opción 3º:

El responsable de iniciar la comunicación es el sujeto concreto, pero se puede dar un problema cuando el objeto concreto está siendo actualizado de forma continua ya que debido a esto se tendía que realizar muchas actualizaciones en muy poco tiempo. La solución sería suspender temporalmente las llamadas al método de actualización/notificación.

Opción 4º (referencias inválidas): A la hora de implementar este patrón debemos

de ser cuidadosos cuando un elemento observable desaparece. En ciertos lenguajes será el gestor de memoria el que cada cierto tiempo debe de limpiará las referencias liberadas, pero si un observable que sigue siendo observado puede no liberarse nunca. Para solucionar este problema puede crearse una función “destruir (destroy) “que notifique al gestor que el elemento observable va a desaparecer y si no estamos usando la variante del gestor el observable directamente des-registrará a sus observadores.

Ejemplo:

Nombre del patrón:State (patrón de diseño)

Problema: Existe una extrema complejidad en el código cuando se intenta administrar comportamientos diferentes según una cantidad de estados diferentes. Asimismo el mantenimiento de este código se torna dificultoso, e incluso se puede llegar en algunos casos puntuales a la incongruencia de estados actuales por la forma de implementación de los diferentes estados en el código (por ejemplo con variables para cada estado).

Solución:

Se implementa una clase para cada estado diferente del objeto y el desarrollo de cada método según un estado determinado. El objeto de la clase a la que le pertenecen dichos estados resuelve los distintos comportamientos según su estado, con instancias de dichas clases de estado. Así, siempre tiene presente en un objeto el estado actual y se comunica con este para resolver sus responsabilidades.

Consecuencias: Se debe contemplar la complejidad comparada con otras soluciones.

Diagrama de clases del patrón:

Context(Contexto): Este integrante define la interfaz con el cliente. Mantiene una instancia de ConcreteState (Estado Concreto) que define su estado actualState (Estado):Define una interfaz para el encapsulamiento de la responsabilidades asociadas con un estado particular de Context.Subclase ConcreteState:Cada una de estas subclases implementa el comportamiento o responsabilidad de Context.

Ejemplo: /*Patrón de diseño Estado - un FSM con dos estados y dos eventos (lógica de transición distribuida - lógica en las clases derivadas

estatales).*/ #include <iostream> #include <string> using namespace std; //1. Creamos la clase maquina que nos dara el estado en que se

encuentra class Machine { class State *current; public: Machine(); void setCurrent(State *s) { current = s; } void on(); void off(); }; //2. Aqui creamos la clase estado, para saber que esta haciendo class State { public: virtual void on(Machine *m) { cout << " already ON\n"; } virtual void off(Machine *m) { cout << " already OFF\n"; } }; void Machine::on() { current->on(this); } void Machine::off() { current->off(this); }

class ON: public State { public: ON() { cout << " ON-ctor "; }; ~ON() { cout << " dtor-ON\n"; }; void off(Machine *m); }; //3. En esta parte creamos la classe OFF, para saver en que momento se apaga la maquina class OFF: public State { public: OFF() { cout << " OFF-ctor "; }; ~OFF() { cout << " dtor-OFF\n"; }; void on(Machine *m) { cout << " going from OFF to ON"; m->setCurrent(new ON()); delete this; } }; void ON::off(Machine *m) { cout << " going from ON to OFF"; m->setCurrent(new OFF()); delete this; } Machine::Machine() { current = new OFF(); cout << '\n'; }

Ejemplo…

int main() { void(Machine:: *ptrs[])() = { Machine::off, Machine::on }; Machine fsm; int num; while (1) { cout << "Enter 0/1: "; //Introducimos la combinacion de digitos

para saver el estado cin >> num; (fsm. *ptrs[num])(); } }

Nombre del Patron:Strategy (Estrategia, Policy).

Problema: El patrón Strategy aborda problemas que

pueden (o se prevee que puedan) ser implementados o afrontados de distintas formas y cuyo interfaz esta bien definido y es común para dichas formas, pudiendo ser cualquiera de ellas valida o más deseable en determinadas situaciones y permitiendo el cambio entre las distintas estrategias en tiempo de ejecución.

Consecuencias:

Factoriza aspectos comunes de una familia de algoritmos y utilizarlos en las clases base de la jerarquía.Aumenta cohesión del clienteSistematiza el uso de implementaciones alternativasEl cliente es el responsable de crear estrategias, por tanto debe comprender las posibilidades que ofrecen, esto es, debe ser relevante para el contexto del cliente.Menor eficiencia. Aumenta el número de objetos creados.

Diagramas de clase del patrón:

Ejemplo:

Ejemplo: public interface Strategy {

public void execute();}public class ConcreteStrategyA implements Strategy {public void execute() { ... }}public class ConcreteStrategyB implements Strategy {public void execute() { ... }}public class Context {private Strategy _strategy;public Context (Strategy s) { _strategy = s; }public Context () { _strategy = new ConcreteStrategyA(); }public void execute() { _strategy.execute(); }}public class Client {public static void main (String args[]) {Context context = new Context(new ConcreteStrategyA());context.execute();}}

Solución

La solución que el patrón estrategia supone para este escenario pasa por encapsular los distintos algoritmos en una jerarquía y que el cliente trabaje contra un objeto intermediario contexto.

El cliente puede elegir el algoritmo que prefiera de entre los disponibles, o el mismo contexto puede ser el que elija el más apropiado para cada situación.

Nombre del patrón: Template Method (patrón de diseño)

Dentro del marco de la programación orientada a objetos, el patrón Template Method (Método Plantilla o Método Modelo en español) es un patrón de diseño enmarcado dentro de los llamados patrones de comportamiento, que se caracteriza por la definición, dentro de una operación de una superclase, de los pasos de un algoritmo, de forma que todos o parte de estos pasos son redefinidos en las subclases herederas de la citada superclase.

Propósito:

Permitir que ciertos pasos de un algoritmo definido en una operación de una superclase, sean redefinidos en las subclases sin necesidad de tener que sobrescribir la operación entera.

Aplicabilidad

La utilización del patrón Método Plantilla es adecuada en los siguientes casos:

• Cuando contamos con un algoritmo con varios pasos que no cambian, de modo que dichos pasos invariantes serían implementados en una superclase, dejando la implementación de los pasos que cambian para las subclases.

• Para evitar la replicación de código mediante generalización: se factoriza el comportamiento común de varias subclases en una única superclase.

• Para controlar las extensiones de las subclases. El Método Plantilla utiliza métodos especiales (métodos de enganche o hooks) en ciertos puntos, siendo los únicos puntos que pueden ser redefinidos y, por tanto, los únicos puntos donde es posible la extensión.

Estructura

Se muestra a continuación la estructura que sigue el patrón Método Plantilla:

Participantes: Clase Abstracta: proporciona la definición de

una serie de operaciones primitivas (normalmente abstractas) que implementan los pasos de un algoritmo y que serán definidas en las subclases.

Se encarga también de la implementación de un método desde el cual son invocadas, entre otras, las operaciones primitivas. Dicho método actúa a modo de plantilla, de ahí el nombre de este patŕon, definiendo la secuencia de operaciones de un algoritmo.

Clase Concreta: implementa las operaciones primitivas definidas en la clase abstracta de la cual hereda, quedando así determinado el comportamiento específico del algoritmo definido en el método plantilla, para cada subclase.

Consecuencias:

• La utilización de este patrón es fundamental a la hora de reutilizar código.

• Se invierte el control: en este caso la superclase es la encargada de llamar a las operaciones definidas en las subclases.

• Distinción entre:• Operaciones primitivas (implementadas en la superclase)• Operaciones de enganche o hooks (proporcionan un código por defecto que puede ser refinado en las subclases).

Cabe destacar que los métodos plantilla juegan un papel clave en las bibliotecas de clases ya que permiten extraer el comportamiento común de las clases de la biblioteca. Otro uso común de este patrón se da en la creación de sistemas de plugins gracias principalmente a la utilización de las anteriormente citadas operaciones de enganche (hooks).

Implementación

A la hora de proceder a implementar este patrón, resulta de interés tener en cuenta los seguintes detalles:

•Es recomendable declarar las operaciones primitivas de tal forma que sólo puedan ser llamadas por el método plantilla (protected si se trabaja con el lenguaje de programación Java)•Debe reducirse en la medida de lo posible el número de operaciones primitivas que van a ser invocadas desde el método plantilla. De este forma se reducirá la complejidad de las subclases y resultará menos tediosa su implementación.

Ejemplo de implementación (Java)

Se muestra a continuación un ejemplo de implementación del patrón Método Plantilla. En el se intenta ilustrar a grandes rasgos el modo de desplazamiento de un automóvil que, básicamente, se puede simplificar en: acelerar, cambiar de marcha y frenar. El proceso de acelerar y frenar se puede considerar que es idéntico en todos los automóviles, sin embargo la forma de cambiar de marcha varia de unos a otros según sean autos con cambio manual o autos con cambio automático.

De acuerdo con esto, podemos considerar una superclase Automóvil en la cual se define un método plantilla Desplazar desde el cual se llama a la operación primitiva CambiarMarcha que es implementada de una forma en la subclase "AutomovilManual", y de otra forma en la subclase "AutomovilAutomatico".

De acuerdo con esto, podemos considerar una superclase Automóvil en la cual se define un método plantilla Desplazar desde el cual se llama a la operación primitiva CambiarMarcha que es implementada de una forma en la subclase "AutomovilManual", y de otra forma en la subclase "AutomovilAutomatico".

Nombre del patrón:Visitor (patrón de diseño)

Problema: Uno de los principales problemas que presenta este diseño, es querer que una operaciónno esté en la jerarquía, sino que esté fuera, para que cada vez que haya un cambio no haya que cambiarlo todo. Esta opción, además, obliga a tener que definir cada operación que necesite el cliente en cada clase de la jerarquía, y a que los clientes conozcan operaciones que no necesita, ya que sólo interesa que conozca las que va a manejar.

Nombre del patrón: Visitor (patrón de diseño)

Problema: Uno de los principales problemas que presenta este diseño, es querer que una operación no esté en la jerarquía, sino que esté fuera, para que cada vez que haya un cambio no haya que cambiarlo todo. Esta opción, además, obliga a tener que definir cada operación que necesite el cliente en cada clase de la jerarquía, y a que los clientes conozcan operaciones que no necesita, ya que sólo interesa que conozca las que va a manejar.

SOLUCIÓN

Es un patrón de comportamiento, que permite definir una operación sobre objetos de una jerarquía de clases sin modificar las clases sobre las que opera. Representa una operación que se realiza sobre los elementos que conforman la estructura de un objeto.

CONSECUENCIAS

Es fácil añadir nuevas operaciones a un programa utilizando Visitantes, ya que el visitante contiene el código en lugar de cada una de las clases individuales. Además, los visitantes pueden recoger las operaciones relacionadas en una sola clase en lugar de obligar a cambiar o derivar clases para agregar estas operaciones. Esto puede hacer al programa más sencillo de escribir y mantener.

Diagrama de clases del patrón: Visitante (Visitor): Declara una operación de visita para cada elemento concreto en

la estructura de objetos, que incluye el propio objeto visitado Visitante Concreto (ConcreteVisitor1/2): Implementa las operaciones del visitante y

acumula resultados como estado local Elemento (Element): Define una operación “Accept” que toma un visitante como

argumento Elemento Concreto (ConcreteElementA/B): Implementa la operación “Accept”

1.- Agregar un método accept (visitantes) a la jerarquía "elemento"2.- Crear una base de clase "turista" con un método de visita () para cada tipo de "elemento"3.- Crear un "visitante" que es una clase derivada para cada "operación" para hacerlo en "elementos"4.- Cliente crea objetos "visitantes" y pasa a cada uno a aceptar una llamada

EJEMPLO