209 lines
5.6 KiB
Markdown
Raw Normal View History

---
layout: pattern
title: Bridge
folder: bridge
permalink: /patterns/bridge/
categories: Structural
language: en
tags:
- Gang of Four
---
## Also known as
2020-08-23 18:53:57 +03:00
Handle/Body
## Intent
2020-08-23 18:53:57 +03:00
2017-08-23 21:25:48 +03:00
Decouple an abstraction from its implementation so that the two can vary independently.
2017-08-23 21:25:48 +03:00
## Explanation
Real world example
2020-08-23 18:53:57 +03:00
> Consider you have a weapon with different enchantments, and you are supposed to allow mixing
> different weapons with different enchantments. What would you do? Create multiple copies of each
> of the weapons for each of the enchantments or would you just create separate enchantment and set
> it for the weapon as needed? Bridge pattern allows you to do the second.
2017-08-23 21:25:48 +03:00
In Plain Words
2020-08-23 18:53:57 +03:00
> Bridge pattern is about preferring composition over inheritance. Implementation details are pushed
> from a hierarchy to another object with a separate hierarchy.
2017-08-23 21:25:48 +03:00
Wikipedia says
> The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently"
**Programmatic Example**
2020-08-23 18:53:57 +03:00
Translating our weapon example from above. Here we have the `Weapon` hierarchy:
2017-08-23 21:25:48 +03:00
```java
2017-08-23 21:25:48 +03:00
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;
}
}
```
2020-08-23 18:53:57 +03:00
Here's the separate enchantment hierarchy:
2017-08-23 21:25:48 +03:00
```java
2017-08-23 21:25:48 +03:00
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.");
}
}
```
2020-08-23 18:53:57 +03:00
Here are both hierarchies in action:
2017-08-23 21:25:48 +03:00
```java
2019-11-11 22:07:54 +02:00
var enchantedSword = new Sword(new SoulEatingEnchantment());
2017-08-23 21:25:48 +03:00
enchantedSword.wield();
enchantedSword.swing();
enchantedSword.unwield();
// The sword is wielded.
// The item spreads bloodlust.
// The sword is swinged.
// The item eats the soul of enemies.
// The sword is unwielded.
// Bloodlust slowly disappears.
2019-11-11 22:07:54 +02:00
var hammer = new Hammer(new FlyingEnchantment());
2017-08-23 21:25:48 +03:00
hammer.wield();
hammer.swing();
hammer.unwield();
// The hammer is wielded.
// The item begins to glow faintly.
// The hammer is swinged.
// The item flies and strikes the enemies finally returning to owner's hand.
// The hammer is unwielded.
// The item's glow fades.
```
## Class diagram
2020-08-23 18:53:57 +03:00
![alt text](./etc/bridge.urm.png "Bridge class diagram")
## Applicability
2020-08-23 18:53:57 +03:00
Use the Bridge pattern when
2020-08-23 18:53:57 +03:00
* You want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time.
* Both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently.
* Changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
* You have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies.
* You want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
## Tutorial
2020-08-23 18:53:57 +03:00
* [Bridge Pattern Tutorial](https://www.journaldev.com/1491/bridge-design-pattern-java)
## Credits
2020-07-06 13:31:07 +03:00
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)