Puente

RECU-0201 (Recurso Patrón)

Descripción

La idea de este patrón es desacoplar una abstracción de su implementación, de manera que ambas puedan ser modificadas independientemente sin necesidad de alterar por ello la otra.

También conocido como Handle, Body

Clasificación

Patrón estructural

Motivación

Cuando una abstracción puede tener varias posibles implementaciones, normalmente hacemos uso de la herencia para acomodar esta necesidad. Una clase abstracta define de la interfaz de la abstracción y las subclases concretas lo implementan. Esto no es los suficientemente flexible. Esto es difícil de mantener y modificar y no podemos reutilizar los componentes. Necesitamos un patrón que solucione esta problemática.

Aplicabildidad

Se usa el patrón 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 como sus implementaciones deben ser extensibles por medio de subclases. En este caso, el patrón permite combinar abstracciones e implementaciones 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.
  • Se desea esconder la implementación de una abstracción completamente a los clientes. En C++, la representación de una clase es visible en la interface 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.

Estructura

 

188

Participantes

  • Abstracción define una interface abstracta. Mantiene una referencia a un objeto de tipo Implementor.
  • AbstracciónRefinada extiende la interface definida por Abstraction
  • Implementador define la interface para la implementación de clases. Esta interface no se tiene que corresponder exactamente con la interface de Abstraction; de hecho, las dos interfaces pueden ser bastante diferente. Típicamente la interface Implementor provee sólo operaciones primitivas, y Abstraction define operaciones de alto nivel basadas en estas primitivas.
  • ImplementadorConcreto implementa la interface de Implementor y define su implementación concreta.

Colaboraciones

Abstraccion emite los pedidos de los clientes a su objeto Implementador.

Consecuencias

  • Desacopla interface e implementación: una implementación no es limitada permanentemente a una interface. 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 Abstraccion 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 Abstraccion y 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.
  • Mejora la extensibilidad: se puede extender las jerarquías de Abstraccion e Implementador independientemente.
  • Esconde los detalles de la implementación a los clientes.

Implementación

Consideremos las siguientes cuestiones de implementación cuando se aplica este patró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 Abstraccion e Implementador. Sin embargo, esta separación es aún muy útil cuando un cambio en la implementación de una clase no debe afectar a sus clientes existente, es decir, ellos no deben ser recompilados, sólo relinkeados.
  • Creando el objeto Implementor adecuado: ¿Cómo, cuándo y dónde que clase Implementor instanciar cuando hay más de una?Si Abstraccion conoce todas las clases ImplementadorConcreto 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.

  • 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 para asociar una interfaz con su implementación.

Creamos una clase Abstracción padre que sea abstracta, además de abstracciones concretas mediante clases que heredan de ella. Por otro lado se tienen las clases que implementan la funcionalidad con una estructura similar: una clase ImplementaciónAbstracta padre, y todas las clases hijas necesarias que implementan la funcionalidad de todas las maneras necesarias. La relación se da entre la clase abstracta Abstracción y la clase abstracta Implementación, delegando la primera la implementación en la segunda, que a su vez la delega en las implementaciones concretas.

Código de ejemplo

/** interfaz que implementan los implementadores especificos **/
public interface Implementador {
public abstract void operacion();
}
/** primera implementacion de Implementador **/
public class ImplementacionA implements Implementador{
public void operacion() {
System.out.println("Esta es la implementacion A");
}
}
/** segunda implementacion de Implementador **/
public class ImplementacionB implements Implementador{
public void operacion() {
System.out.println("Esta es una implementacion de B");
}
}
/** interfaz de abstracción **/
public interface Abstraccion {
public void operacion();
}
/** clase refinada que implementa la abstraccion **/
public class AbstraccionRefinada implements Abstraccion{
private Implementador implementador;
public AbstraccionRefinada(Implementador implementador){
this.implementador = implementador;
}
public void operacion(){
implementador.operacion();
}
}
/** aplicacion que usa el patrón Bridge **/
public class EjemploBridge {
public static void main(String[] args) {
Abstraccion[] abstracciones = new Abstraccion[2];
abstracciones[0] = new AbstraccionRefinada(new ImplementacionA());
abstracciones[1] = new AbstraccionRefinada(new ImplementacionB());
for(Abstraccion abstraccion:abstracciones)
abstraccion.operacion();
}
}

Usos conocidos

  • El DOM de KDE3 esta basado en un bridge
  • ET++ , framework para aplicaciones

Patrones Relacionados

  • El patrón Adaptador tiene también como objetivo hacer trabajar juntas clases con distinta interfaz, pero en general se aplica a sistemas que existen. El patrón puente se suele aplicar al empezar un diseño, para permitir que las abstracciones e implementaciones evolucionen independientemente 

Contenidos relacionados

Recursos
Área: Desarrollo » Aplicaciones Java » Otras Especificaciones de Codificación de Aplicaciones Java
Código Título Tipo Carácter
RECU-0191 Factoría Abstracta Patrón Recomendado
RECU-0181 Adaptador Patrón Recomendado