Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

28
Javier Juárez González Javier Juárez González José Carlos Bernárdez José Carlos Bernárdez Fdez. Fdez. Alberto Barbosa León Alberto Barbosa León

Transcript of Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

Page 1: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

Javier Juárez González Javier Juárez González José Carlos Bernárdez José Carlos Bernárdez Fdez.Fdez.Alberto Barbosa LeónAlberto Barbosa León

Page 2: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

INTENCIÓNDesacoplar una abstracción de su implementación, de manera que ambas puedan ser modificadas independientemente sin necesidad de alterar por ello la otra.

Page 3: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

INTENCIÓN

Es decir, se desacopla una abstracción de su implementación para que puedan variar independientemente.

Page 4: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

MOTIVACIÓN

Cuando una abstracción se puede implementar de varias maneras, lo normal para adaptarlas es usar herencia.

Una clase abstracta define la interfaz a la abstracción y son las subclases las que las implementan de diferentes maneras.

Page 5: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

MOTIVACIÓN

Pero usar la herencia a veces no es suficientemente flexible, ya que une la abstracción y la implementación de forma permanente y esto hace dificil la modificación, extensión y reutilización de ambas de forma independiente.

Page 6: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

EJEMPLO

Page 7: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

MOTIVACIÓN

Si queremos añadir una clase que pinte en ambas ventanas tenemos que crear una por cada tipo de ventana.

Solo la implementación de la ventana debería depender de la plataforma en la que la aplicación corre.

Necesitamos proporcionar una interfaz estable, uniforme y cerrada al no permitir cambios directos del código, pero abierta al permitir la ampliación.

El patrón Bridge soluciona este problema poniendo la abstracción y su implementación en jerarquías de clases separadas.

Page 8: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.
Page 9: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

APLICABILIDADSe usa el patrón Bridge cuando: Se desea evitar un enlace permanente entre la abstracción y su implementación. Esto puede ser debido a que la implementación debe ser seleccionada o cambiada en tiempo de ejecución. Tanto las abstracciones y sus implementaciones deben ser extensibles por medio de subclases. En este caso, el patrón Bridge permite combinar abstracciones e implementciones diferentes y extenderlas independientemente. Cambios en la implementación de una abstracción no deben impactar en los clientes, es decir, su código no debe tener que ser recompilado.

Page 10: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

APLICABILIDAD

(En C++) Se desea esconder la implementación de una abstracción completamente a los clientes. La representación de una clase es visible en la interfaz de la clase.

Se desea compartir una implementación entre múltiples objetos (quizá usando contadores), y este hecho debe ser escondido a los clientes.

Page 11: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

ESTRUCTURA

Page 12: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

PARTICIPANTES

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

RefinedAbstraction extiende la interface definida por Abstraction.

Implementor define la interface para la implementación de clases. Esta interface no se tiene que corresponder exactamente con la interface de Abstraction. Típicamente la interface Implementor provee sólo operaciones primitivas, y Abstraction define operaciones de alto nivel basadas en estas primitivas.

ConcreteImplementor implementa la interface de Implementor y define su implementación concreta.

Page 13: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

COLABORACIONES

Abstraction emite los pedidos de los clientes a su objeto Implementor.

Page 14: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CONSECUENCIAS

1.Desacopla interfaz e implementación: una implementación no es vinculada permanentemente a una interfaz. La implementación de una abstracción puede ser configurada en tiempo de ejecución. Además le es posible a un objeto cambiar su implementación en tiempo de ejecución. Desacoplando Abstraction e Implementor también elimina las dependencias sobre la implementación en tiempo de compilación. Cambiar una clase de implementación no requiere recompilar la clase Abstraction ni sus clientes. Esta propiedad es esencial cuando te debes asegurar la compatibilidad binaria entre diferentes versiones de una biblioteca de clases. Es más, este desacoplamiento fomenta las capas, que pueden conducir a un sistema mejor estructurado. La parte de alto nivel de un sistema sólo tiene que conocer Abstraction e Implementor.

2.Mejora la extensibilidad: se puede extender las jerarquías de Abstraction e Implementor independientemente.

3.Esconde los detalles de la implementación a los clientes.

Page 15: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

IMPLEMENTACIÓN

Sólo un Implementor. En situaciones donde existe sólo una implementación, crear una clase Implementor abstracta no es necesario.

Esto es un caso especial del patrón; hay una relación uno-a-uno entre Abstraction e Implementor. Sin embargo, esta separación es útil cuando un cambio en la implementación de una clase no debe afectar a sus clientes, es decir, ellos no deben ser recompilados, sólo reenlazados.

En C++, la interfaz de la clase Implementor puede ser definida en un archivo header privado el cual no es proveído a los clientes. Esto permite esconder una implementación de una clase completamente de sus clientes.

Page 16: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

IMPLEMENTACIÓN

Creando el objeto Implementor adecuado. ¿Cómo, cuándo y dónde que clase Implementor instanciar cuando hay más de una?

Si Abstraction conoce todas las clases ConcreteImplementor puede instanciar una de ellas en su constructor; puede decidir cuál instanciar dependiendo de los parámetros del constructor.

Otra aproximación es elegir una implementación inicial por defecto y cambiarla después acorde al uso.

También es posible delegar la decisión a otro objeto en conjunto.

Page 17: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

IMPLEMENTACIÓN

Compartiendo implementadores: el estilo Handle/Body en C++ puede ser usado para compartir implementaciones de muchos objetos. Body almacena una cuenta de referencia que la clase Handle incrementa y decrementa.

Usando herencia múltiple. Se puede usar herencia múltiple en C++ para asociar una interfaz con su implementación.

Page 18: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLO

Sigamos con el ejemplo anterior y su implementación en C++ a partir del patrón Bridge.

El problema tiene que ver con la implementación de una abstracción portable llamada Window para una interfaz de usuario.Esta abstracción nos deberá servir para aplicaciones que trabajan con dos plataformas, X Window y PM de IBM, por ejemplo.

Usando herencia, tendremos una clase abstracta Window y dos subclases que implementen ésta para cada plataforma

Page 19: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLO

Pero esto se puede volver alarmantemente peligroso en algunos casos. Por ejemplo, si queremos crear una subclase IconWindow de Window que dibuje iconitos, para que sea soportada por ambas plataformas deberemos implementar dos nuevas clases, XIconWindow y PMIconWindow. Esto se repetirá cada vez que añadamos un tipo de ventana.

Page 20: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLO

Tenemos un terrible problema, el código cliente es ahora dependiente de la plataforma, error si se necesita código portable.

El cliente debería ser capaz de crear una ventana sin decidir una implementación concreta. Sólo la implementación de ventana debería depender de la plataforma en la que corre la aplicación.

Entonces el código cliente debería instanciar una ventana sin mencionar una plataforma específica.

Usando el patrón Bridge solucionamos el problema poniendo la abstracción Window y su implementación en jerarquías de clases separadas.

Page 21: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLO

Hay una jerarquía de clases para las interfaces ventana y una jerarquía separada para las implementaciones de ventana dependiendo de la plataforma

Page 22: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLOVemos ahora el código en C++ que implementa el ejemplo.La clase Window define la abstracción ventana para las oplicaciones cliente:class Window{

Public:

Window(View* contents);

virtual void DrawContents()

virtual void Open();

virtual void Close();

virtual void SetOrigin(const Point& at);

virtual void SetExtent(const Point& extent);

virtual void DrawLine(const Point&, const Point&);

protected:

WindowImp* GetWindowImp();

View* GetView();

private:

WindowImp* _imp;

View* _contents;

};

Page 23: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLOclass WindowImp{

public:

virtual void ImpTop();

virtual void ImpBottom();

virtual void DeviceRect(Coord, Coord, Coord, Coord) = 0;

virtual void DeviceText(cons char*, Coord, Coord) =0;

protected:

WindowImp();

};

Subclases de Window definen distintos tipos de ventana que la aplicación podrá usar, veamos por ejemplo la clase AplicationWindow

Page 24: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLO

Class ApplicationWindow : public Window{

Public:

virtual void DrawContents();

};

void ApplicationWindow :: DrawContents(){

GetView()->DrawOn(this);

}

Subclases concretas de WindowImp soportan diferentes sistemas de ventanas. La subclase XWindowImp soporta el sistema X Window System, al igual que PMWindowImp soporta Presentation Manager (PM). Un ejemplo:

Page 25: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLO

class XWindowImp : public WindowImp{

public:

XWindowImp();

virtual void DeviceRect(Coord, Coord, Coord, Coord);

private:

Display* _dpy;

Drawable _winid;

GC _gc;

};

Estas subclases implementan las operaciones de WindowImp en términos de primitivas de un sistema de ventanas. Por ejemplo, DeviceRect es implementado para X como sigue:

Page 26: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

CÓDIGO DE EJEMPLOvoid XWindowImp :: DeviceRect{

Coord x0, Coord y0, Coord x1, Coord y1)

{

int x = round(min(x0,x1));

int y = round(min(y0,y1));

int w = round(abs(x0 – x1));

int h = round(abs(y0 – y1));

XDrawRectangle(_dpy, _winid, _gc, x, y, w, h);

}

Y la pregunta buena es ¿Cómo una ventana obtiene una instancia de la subclase correcta de WindowImp?

Asumiremos que Window tiene esa responsabilidad en este ejemplo. Una operación GetWindowImp devolverá la correcta instancia de un Abstract Factory que eficientemente encapsula todos los sistemas de ventanas específicos.

Page 27: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

USOS CONOCIDOS

Ademas del uso visto en JDBC de Java ademas podemos encontrar ejemplos en NeXT’s appkid que usa el patron Bridge en la implementacion y muestra de imágenes gráficas.

También es utilizado en la librería libg++ .

Page 28: Javier Juárez González José Carlos Bernárdez Fdez. Alberto Barbosa León.

PATRONES RELACIONADOS

Una Abstract Factory puede crear y configurar un Bridge particular.