Saltar al contenido principal

Bridge

StructuralGang of FourAlrededor de 2 min

También conocido como

Handle/Body

Intención

Desacoplar una abstracción de su implementación para que ambas puedan variar de forma independiente.

Explicación

Real-world example

Imagina que tienes un arma con diferentes encantamientos, y se supone que debes permitir la mezcla de diferentes armas
con diferentes encantamientos. ¿Qué harías? ¿Crearías múltiples copias de cada una de las armas para cada uno de los
encantamientos o simplemente crearías un encantamiento separado y lo establecerías para el arma según sea necesario?
El
patrón Bridge te permite hacer lo segundo.

En palabras sencillas

El patrón Bridge se trata de preferir la composición sobre la herencia. Los detalles de implementación se empujan de
una jerarquía a otro objeto con una jerarquía separada.

Wikipedia dice

El patrón bridge es un patrón de diseño utilizado en ingeniería de software que tiene como objetivo "desacoplar una
abstracción de su implementación para que las dos puedan variar independientemente"

Ejemplo programático

Transladando nuestro ejemplo de arma de arriba. Aquí tenemos la interfaz arma Weapon:

public interface Weapon {
  void wield();
  void swing();
  void unwield();
  Enchantment getEnchantment();
}

public class Sword implements Weapon {

  private final Enchantment enchantment;

  public Sword(Enchantment enchantment) {
    this.enchantment = enchantment;
  }

  @Override
  public void wield() {
    LOGGER.info("The sword is wielded.");
    enchantment.onActivate();
  }

  @Override
  public void swing() {
    LOGGER.info("The sword is swinged.");
    enchantment.apply();
  }

  @Override
  public void unwield() {
    LOGGER.info("The sword is unwielded.");
    enchantment.onDeactivate();
  }

  @Override
  public Enchantment getEnchantment() {
    return enchantment;
  }
}

public class Hammer implements Weapon {

  private final Enchantment enchantment;

  public Hammer(Enchantment enchantment) {
    this.enchantment = enchantment;
  }

  @Override
  public void wield() {
    LOGGER.info("The hammer is wielded.");
    enchantment.onActivate();
  }

  @Override
  public void swing() {
    LOGGER.info("The hammer is swinged.");
    enchantment.apply();
  }

  @Override
  public void unwield() {
    LOGGER.info("The hammer is unwielded.");
    enchantment.onDeactivate();
  }

  @Override
  public Enchantment getEnchantment() {
    return enchantment;
  }
}

Aquí está la interfaz de encantamientos Enchantment separada:

public interface Enchantment {
  void onActivate();
  void apply();
  void onDeactivate();
}

public class FlyingEnchantment implements Enchantment {

  @Override
  public void onActivate() {
    LOGGER.info("The item begins to glow faintly.");
  }

  @Override
  public void apply() {
    LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
  }

  @Override
  public void onDeactivate() {
    LOGGER.info("The item's glow fades.");
  }
}

public class SoulEatingEnchantment implements Enchantment {

  @Override
  public void onActivate() {
    LOGGER.info("The item spreads bloodlust.");
  }

  @Override
  public void apply() {
    LOGGER.info("The item eats the soul of enemies.");
  }

  @Override
  public void onDeactivate() {
    LOGGER.info("Bloodlust slowly disappears.");
  }
}

Aquí están ambas interfaces en acción:

LOGGER.info("The knight receives an enchanted sword.");
var enchantedSword = new Sword(new SoulEatingEnchantment());
enchantedSword.wield();
enchantedSword.swing();
enchantedSword.unwield();

LOGGER.info("The valkyrie receives an enchanted hammer.");
var hammer = new Hammer(new FlyingEnchantment());
hammer.wield();
hammer.swing();
hammer.unwield();

Aquí está la salida en consola.

The knight receives an enchanted sword.
The sword is wielded.
The item spreads bloodlust.
The sword is swung.
The item eats the soul of enemies.
The sword is unwielded.
Bloodlust slowly disappears.
The valkyrie receives an enchanted hammer.
The hammer is wielded.
The item begins to glow faintly.
The hammer is swung.
The item flies and strikes the enemies finally returning to owner's hand.
The hammer is unwielded.
The item's glow fades.

Diagrama de Clases

alt text
Bridge diagrama de clases

Aplicabilidad

Usa el patrón Bridge cuando

  • Quieres evitar una vinculación permanente entre una abstracción y su implementación. Este podría ser el caso, por
    ejemplo, cuando la implementación debe ser seleccionada o cambiada en tiempo de ejecución.
  • Tanto las abstracciones como sus implementaciones deberían ser extensibles mediante la herencia. En este caso, el
    patrón Bridge te permite combinar las diferentes abstracciones e implementaciones y extenderlas de forma
    independiente.
  • Los cambios en la implementación de una abstracción no deberían tener impacto en los clientes; es decir, su código no
    debería tener que ser recompilado.
  • Tienes una proliferación de clases. Tal jerarquía de clases indica la necesidad de dividir un objeto en dos partes.
    Rumbaugh usa el término "generalizaciones anidadas" para referirse a tales jerarquías de clases.
  • Quieres compartir una implementación entre varios objetos (quizás usando conteo de referencias), y este hecho debería
    ser ocultado al cliente. Un ejemplo simple es la clase String de Coplien, en la que varios objetos pueden compartir la
    misma representación de cadena.

Tutoriales

Créditos