Skip to main content

Bridge Pattern

Separate abstraction from implementation to vary them independently

Pattern Overview

🌉 Bridge Pattern

The Bridge Pattern decouples an abstraction from its implementation so that both can vary independently. It creates a "bridge" between the abstraction and implementation hierarchies.

Core Concepts

🔹 Abstraction - High-level interface that clients use
🔹 Implementation - Low-level interface for concrete operations
🔹 Bridge - Connection that allows abstraction to use any implementation
🔹 Independence - Both sides can evolve separately

Real-World Applications

Graphics Systems - Shapes can use different rendering engines (SVG, Canvas, OpenGL) Database Access - Repository pattern works with different database drivers Messaging Systems - Message types can use different delivery channels Device Drivers - Abstract device operations work with specific hardware

Implementation Benefits

Platform independence - Switch implementations without changing client code
Runtime flexibility - Change implementation at runtime
Separate concerns - Abstraction and implementation evolve independently
Reduces coupling - Client depends only on abstraction interface

Examples:
Render shapes using different graphics APIs (SVG, Canvas) without changing shape code
Input:
circle.draw() with SVG and Canvas renderers
Output:
Different rendering outputs based on implementation
Send different message types through various delivery channels
Input:
textMessage.send() with email and Slack senders
Output:
Messages sent through different channels
Use repository pattern with different database engines
Input:
repository.findUser() with MySQL and PostgreSQL
Output:
Database-specific SQL queries

Concepts

design patternssoftware architecturecode organizationobject-oriented programming

Complexity Analysis

Time:O(1)
Space:O(1)

Implementation

graphics-rendering

Time: O(1) | Space: O(1)
// Implementation hierarchy
interface DrawingAPI {
  drawCircle(x: number, y: number, radius: number): string;
  drawRectangle(x: number, y: number, width: number, height: number): string;
}

class SVGRenderer implements DrawingAPI {
  drawCircle(x: number, y: number, radius: number): string {
    return `<circle cx="${x}" cy="${y}" r="${radius}" />`;
  }
  
  drawRectangle(x: number, y: number, width: number, height: number): string {
    return `<rect x="${x}" y="${y}" width="${width}" height="${height}" />`;
  }
}

class CanvasRenderer implements DrawingAPI {
  drawCircle(x: number, y: number, radius: number): string {
    return `ctx.arc(${x}, ${y}, ${radius}, 0, 2 * Math.PI);`;
  }
  
  drawRectangle(x: number, y: number, width: number, height: number): string {
    return `ctx.fillRect(${x}, ${y}, ${width}, ${height});`;
  }
}

// Abstraction hierarchy  
abstract class Shape {
  protected renderer: DrawingAPI;
  
  constructor(renderer: DrawingAPI) {
    this.renderer = renderer;
  }
  
  setRenderer(renderer: DrawingAPI): void {
    this.renderer = renderer;
  }
  
  abstract draw(): string;
}

class Circle extends Shape {
  constructor(private x: number, private y: number, private radius: number, renderer: DrawingAPI) {
    super(renderer);
  }
  
  draw(): string {
    return this.renderer.drawCircle(this.x, this.y, this.radius);
  }
}

class Rectangle extends Shape {
  constructor(private x: number, private y: number, private width: number, private height: number, renderer: DrawingAPI) {
    super(renderer);
  }
  
  draw(): string {
    return this.renderer.drawRectangle(this.x, this.y, this.width, this.height);
  }
}

// Usage
const svgRenderer = new SVGRenderer();
const canvasRenderer = new CanvasRenderer();

const circle = new Circle(100, 100, 50, svgRenderer);
console.log(circle.draw()); // SVG output

circle.setRenderer(canvasRenderer);
console.log(circle.draw()); // Canvas output