Saltar a: navegación, buscar

Bridge

Bridge
Información sobre la plantilla
Bridge2.gif
Concepto:Técnica usada en programación para desacoplar una abstracción de su implementación

Bridge. También conocido como Handle/Body, es una técnica usada en programación para desacoplar una abstracción de su implementación, de manera que ambas puedan ser modificadas independientemente sin necesidad de alterar por ello la otra.

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

Clasificación

  • Patrón estructural.

Intención

  • Desacopla la abstracción de su implementación permitiendo que las dos varíen independientemente.

Motivo

Cuando una abstracción puede tener varias implementaciones posibles el camino habitual para acomodarlas es usar herencia. Una clase abstracta define la interfaz y clases concretas la implementa de diferentes formas. El problema es que esta solución no es lo suficientemente flexible. La herencia ata una implementación a la abstracción permanentemente, lo que hace la hace difícil de modificar, extender y rehusar la implementación y la abstracción independientemente. La respuesta a esta flexibilidad esta en utilizar el patrón de diseño bridge.

Aplicaciones

  • 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.
  • Hacer que tanto las abstracciones como sus implementaciones deben ser extensibles por medio de subclases. En este caso, el patrón Bridge permite combinar abstracciones e implementaciones diferentes y extenderlas independientemente.
  • Los 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.
  • (En C++) 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.
  • Compartir una implementación entre múltiples objetos (quizá usando contadores), y este hecho debe ser escondido a los clientes.

Estructura

Bridge2.jpg

Participantes

  • Abstraction: define a una interfaz. 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 se tiene que corresponder exactamente con la interfaz de Abstraction; de hecho, las dos interfaces pueden ser muy diferentes. Típicamente la interfaz Implementor provee sólo operaciones primitivas, y Abstraction define operaciones de alto nivel basadas en estas primitivas.
  • ConcreteImplementor: implementa la interfaz de Implementor y define su implementación concreta.

Colaboraciones

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

Consecuencias

  • 1.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 Abstraction e Implementor también elimina las dependencias sobre la implementación en tiempo de compilación. Cambiar una clase de implementación no require 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.

Implementación

Consideremos las siguientes cuestiones de implementación cuando se aplica este patrón:

  • 1.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 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. En C++, la interface 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.
  • 2 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.

  • 3 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.
  • 4 Usando herencia múltiple. Se puede usar herencia múltiple en C++ para asociar una interfaz con su implementación.

Código ejemplo

El código ejemplo se basa en la abstracción de una operación sencilla, de dos números, las implementaciones son sumar, multiplicar y dividir.

 
//esta es la interfaz de la implementacion (implementor)
 
public interface Implementor {
  public int operacion(int a, int b);
 
}
 
//esta es una implementación concreta (concretImplementor)
 
public class Sumar implements Implementor {
   public int operacion(int a, int b) {
        return a+b;
   }
}
 
//esta es una implementación concreta (concretImplementor)
 
public class Multiplicar implements Implementor {
      public int operacion(int a, int b) {
             return a*b;
      }
}
 
// esta es una implementación concreta (concretImplementor)
 
public class Dividir implements Implementor {
       public int operacion(int a, int b) {
              return a/b;
       }
}
//esta es la interfaz de la abstraccion (abstraction)
 
public interface Abstraction {
      public int operar(int num1, int num2);
 
//esta clase esxtiende la interfaz definida por abstraction (RedefinedAbstraction)
 
import operacion.Implementor;
public class RefinedAbstraction implements Abstraction {
      Implementor implementacion=null;
 
   public RefinedAbstraction(Implementor imp){
      this.implementacion=imp;
 
      }
 
   public int operar(int num1, int num2) {
      return implementacion.operacion(num1,num2);
   }
 
}
///Es la clase cliente que utiliza la abstraccion (Client)
 
import operacion.Implementor;
 
import operacion.Abstraction;
 
public class Cliente {
 
     public static void main(String[] args) {
     int num1=4;
     int num2=2;
     Implementor [] imp= {new Sumar(),new Multiplicar(), new Dividir()};
     Abstraction abst[] ={ new RefinedAbstraction(imp[0]), new RefinedAbstraction(imp[1]), new RefinedAbstraction(imp[2])};
     for (int i=0; i<3;i++){
     System.out.println(abst[i].operar(num1, num2));
     }
   }
}

Usos conocidos

  • En ET++, se usa para la abstracción del tipo de ventana.
  • Next’s AppKit usa el patrón en la implementación y representación de imágenes gráficas.

Patrones relacionados

  • Adapter tiene una estructura similar pero su intención es diferente. El patrón bridge separa la abstracción de la implementación de un componente, permitiendo que sean modificados independientemente, mientras adapter reutiliza un componente ya existente que no es compatible.

Fuentes