GoF – Patrones de diseño (IX): Bridge

El siguiente de los patrones que vamos a ver es el Bridge. El objetivo de este patrón es la separación (desacoplamiento) de una abstracción de su implementación, de tal forma que ambas puedan ser modificadas independientemente sin a fectar la una a la otra. Bien, ¿y qué significa esto? Porque seguro que os lo estáis preguntando o al menos, yo si que lo hice la primera (segunda y tercera) vez que leí la frase. Por ponerlo en palabras más entendible o intentar explicar la idea, vamos a comentar primero el caso habitual. Imaginemos una clase abstracta a partir de la cual, posteriormente, vamos a implementar por mecanismos de herencia varia subclases. En este caso, tanto la clase abstracta (que está definiendo el interfaz), como las subclases implementadas, están fuertemente ligadas lo que nos hace perder mucha flexibilidad a la hora de modificar, extender o reusar todo este conjunto. Pues bien, este problema de flexibilidad es lo que viene a solicitar este patrón Bridge colocando en diferentes jerarquias la clase abstracta y sus implementaciones.

Utilizamos este patrón cuando:

  • Se quiere evitar un enlace permanente entre la abstracción y su implementación. Este podría ser el caso, por ejemplo, cuando una implementación debe ser seleccionada o cambiada en tiempo de ejecución.
  • Cuando la abstracción y su implementación deben poderse extender creando nuevas subclases. En este caso, el patrón nos permite combinar diferentes abstracciones e implementaciones y extender todas ellas de forma independiente.
  • Los cambios en la implementación de una abstracción no deberían tener impacto en los clientes (el código no debería tener que ser recompilado).
  • Los cambios en la implementación no deberían ser visibles para los clientes.
  • Cuando se quiere compartir una implementación entre múltiples clientes, pero no se desea que el cliente sea conciente de esto.

Los elementos implicados en este patrón son:

  • Abstraction: Define la clase abstracta que será la interfaz y mantiene la referencia al objeto de tipo Implementor.
  • RefinedAbstraction: Extiende la intefaz definida por Abstraction.
  • Implementator: Define la interfaz para la implementación de las clases. No tiene porque tener correspondencia con el interfaz Abstraction. De hecho, suelen ser diferentes. Por lo general, Implementor facilita las operaciones primitivas,y Abstraction define las operaciones de alto nivel basadas en esas primitivas.
  • ConcreteImplementor: Implementa Implementor creando ya una clase para su utilización.

Las consequencias de la aplicación de este patrón son el desacoplamiento de la interfaz y de su implementación, pudiendo cambiar estos incluso en tiempo de ejecución. Además, se eliminan dependencias de compilación y se consigue una arquitectura estructurada en más niveles (esto, no se si es del todo bueno, pero…) Otra de las cosas que si que nos aporta es la flexibilidad ya que se puede heredar de una u otra pudiendo hacer diferentes jerarquias. Y finalmente, nos aporta el esconder detalles de implementación a los clientes.

Pero como siempre, vamos a ver un poco en código como queda esto, que yo sigo pensando que es una de las mejores formas de entender finalmente la idea. Vamos unos mandos a distancia de televisión.

Implementator:

public interface TV {
    public void on();
    public void off();
    public void tunechannel(int);
}

Clase interfaz para las diferentes marcas de televisores.

ConcreteImplementator:

public class Sony implements TV {
    public void on() { ... }
    public void off() { ... }
    public void tuneChannel(int channel) { ... }
}

ConcreteImplementator:

public class LG implements TV {
    public void on() { ... }
    public void off() { ... }
    public void tuneChannel(int channel) { ... }
}

Estos serán nuestros modelos especificos de televisiones.

Abstraction:

public abstract class RemoteControl {
    private TV tv;
    public void on() { tv.on(); }
    public void off() { tv.off(); }
    public void tuneChannel(int channel) { tv.tuneChannel(channel); }
}

Con esto tendríamos definido nuestro control remoto.

RefinedAbstraction:

public class RemoteControlPlus implements RemoteControl {
    private int channel;
    public void next() { channel++; }
    public void previous() { channel--; }
}

Con esto le hemos añadido a nuestor control remoto más funcionalidades sin tener que modificar nada más en la jerarquia de clases, ni haber modificado nada en el Implementator. Esto, como ya se ha dicho antes, nos aporta más flexibilidad, pero genera más complejidad y niveles en nuestra estructura jerarquica lo cual no siempre es bueno. Pero bueno, como siempre, hay que ponderar todas las decisiones que tomemos y ver si nos aportan más beneficios o más problemas.

Otros patrones relacionados con este son el de Abstract Factory que puede crear y configurar un Bridge particular, y el patrón Adapter que sele ser muy util para hacer que clases no relacionadas trabajen juntas.

Hasta aquí todo por hoy. Nos vemos.

GoF – Patrones de diseño (IX): Bridge

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.