Bridge
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
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.