Low-level design2 min

Bridge Pattern

Type: Structural Pattern
Relevance: Medium. Very useful for preventing class explosion in complex hierarchies.

The Bridge pattern lets you split a large class or a set of closely related classes into two separate hierarchies—Abstraction and Implementation—which can be developed independently of each other.

Real-Life Analogy

Think of Remote Controls and Devices. You have a Remote (Abstraction) and a TV (Implementation). A remote control has buttons like power() and volumeUp(). The TV has the actual logic to execute those commands. If you also have a Radio, and you have two types of remotes (BasicRemote and AdvancedRemote), creating a subclass for every combination (BasicTVRemote, AdvancedTVRemote, BasicRadioRemote, AdvancedRadioRemote) leads to class explosion.

Instead, the Bridge pattern says: The Remote has a reference to a Device. The Remote delegates the work to the Device.

The Problem

When you try to extend a class in two independent dimensions (e.g., Shape and Color), inheritance gets messy. If you have Circle and Square, and you want them in Red and Blue, you end up with RedCircle, BlueCircle, RedSquare, BlueSquare. Add another shape and another color, and the number of classes grows exponentially.

How to Implement Bridge

Separate the dimensions into two hierarchies. Extract the color-related code into its own class with a Color interface, and have the Shape class maintain a reference to an object of that interface.

Example in Code

typescript
// 1. The Implementation (Dimension 1)
interface Color {
  fill(): string;
}

class Red implements Color {
  fill(): string { return "Red"; }
}

class Blue implements Color {
  fill(): string { return "Blue"; }
}

// 2. The Abstraction (Dimension 2)
abstract class Shape {
  // The Bridge! The Shape holds a reference to the Implementation
  protected color: Color;

  constructor(color: Color) {
    this.color = color;
  }

  abstract draw(): void;
}

class Circle extends Shape {
  draw(): void {
    console.log(`Drawing a ${this.color.fill()} Circle.`);
  }
}

class Square extends Shape {
  draw(): void {
    console.log(`Drawing a ${this.color.fill()} Square.`);
  }
}

// Client Code
const red = new Red();
const blue = new Blue();

const redCircle = new Circle(red);
const blueSquare = new Square(blue);

redCircle.draw(); // Drawing a Red Circle
blueSquare.draw(); // Drawing a Blue Square

Class Diagram

Why is it Important?

  • Prevents Class Explosion: If you have N shapes and M colors, instead of creating $N \times M$ classes, you only create $N + M$ classes.
  • Independent Extension: You can add new Shapes or new Colors completely independently of one another.