Design pattern "Bridge"

Let's learn what is the "Bridge" design pattern πŸŒ‰
Thursday, July 4, 2024

Real world example

Let’s consider a scenario where you’re building a web application that has different types of UI components like Buttons, Panels, and Modals. Each of these components can have different themes, like DarkTheme, LightTheme, or CustomTheme.

Without the Bridge pattern, you might end up creating separate classes for each combination like DarkThemeButton, LightThemeButton, DarkThemePanel, LightThemePanel, and so on. This approach quickly becomes unmanageable as the number of components and themes grow.

With the Bridge pattern, you can separate the abstraction (UI components) from the implementation (themes).

Before

Loading graph...

After

Loading graph...

In plain words

Bridge pattern is about preferring composition over inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy.

Wikipedia definition

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

//Implementor
interface Theme {
public String getColor();
}
//Concrete Implementors
class DarkTheme implements Theme {
@Override
public String getColor() {
return "Dark";
}
}
class LightTheme implements Theme {
@Override
public String getColor() {
return "Light";
}
}
//Abstraction
abstract class UIComponent {
protected Theme theme;
public UIComponent(Theme theme) {
this.theme = theme;
}
abstract public void render();
}
//Refined Abstractions
class Button extends UIComponent {
public Button(Theme theme) {
super(theme);
}
@Override
public void render() {
System.out.println("Rendering Button in " + theme.getColor() + " theme");
}
}
class Panel extends UIComponent {
public Panel(Theme theme) {
super(theme);
}
@Override
public void render() {
System.out.println("Rendering Panel in " + theme.getColor() + " theme");
}
}
//Main
class Main {
public static void main(String[] args) {
UIComponent darkButton = new Button(new DarkTheme());
darkButton.render();
UIComponent lightPanel = new Panel(new LightTheme());
lightPanel.render();
}
}

Easy example diagram

Here is an another example of the bridge design pattern. Instead of having to manage a multitude of classes like BlueRectangle, RedRectangle, BlueCircle, RedCircle, we will prefer separate the classes and prioritize composition over inheritance.

Before

Loading graph...

After

Loading graph...

Some other examples

Here are some scenarios where the Bridge design pattern would be a good fit:

  1. Graphics: Different graphic rendering engines (OpenGL, DirectX) can be switched at runtime, but the high-level drawing functions remain the same.
  2. Databases: Different databases (MySQL, PostgreSQL, SQLite) can be used interchangeably without changing the way queries are executed.
  3. User Interfaces: Different user interface libraries (Qt, GTK) can be used interchangeably without changing the high-level UI manipulation functions.
  4. Operating Systems: Different operating systems (Windows, Linux, MacOS) can be used interchangeably without changing the high-level system calls.
  5. Payment Gateways: Different payment gateways (PayPal, Stripe, Square) can be used interchangeably without changing the high-level payment processing functions.
  6. Messaging Systems: Different messaging systems (RabbitMQ, Kafka, ActiveMQ) can be used interchangeably without changing the high-level message sending and receiving functions.
  7. Cloud Providers: Different cloud providers (AWS, Google Cloud, Azure) can be used interchangeably without changing the high-level resource management functions.

Recommended articles