Back to Overview

Bridge Pattern

The Bridge Pattern separates an abstraction from its implementation so that both can vary independently. It's a structural design pattern that helps 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.

Think of it as building a bridge between two sides: abstractions (what the client sees) and implementations (how things are done behind the scenes).

Problem

Imagine you're developing a shape rendering system. You have different shapes (circle, square) and different rendering methods (vector, raster). If you create a class for each combination, you end up with an explosion of classes: VectorCircle, RasterCircle, VectorSquare, RasterSquare, etc. Adding a new shape or rendering method requires adding multiple new classes.

Abstraction Refined Abstraction A Refined Abstraction B Implementation Concrete Impl. X Concrete Impl. Y Bridge Can use any implementation Can be used by any abstraction

Components

Implementation Example

Let's implement a drawing application that can draw different shapes using different rendering methods:

// Implementation interface
class Renderer {
    renderCircle(radius) {
        // This method should be implemented by concrete renderers
    }
    
    renderSquare(side) {
        // This method should be implemented by concrete renderers
    }
}

// Concrete Implementations
class VectorRenderer extends Renderer {
    renderCircle(radius) {
        return `Drawing a circle of radius ${radius} using vector graphics`;
    }
    
    renderSquare(side) {
        return `Drawing a square with side ${side} using vector graphics`;
    }
}

class RasterRenderer extends Renderer {
    renderCircle(radius) {
        return `Drawing a circle of radius ${radius} using raster pixels`;
    }
    
    renderSquare(side) {
        return `Drawing a square with side ${side} using raster pixels`;
    }
}

// Abstraction
class Shape {
    constructor(renderer) {
        this.renderer = renderer;
    }
    
    draw() {
        // This method should be implemented by refined abstractions
    }
    
    resize(percentage) {
        // This method should be implemented by refined abstractions
    }
}

// Refined Abstractions
class Circle extends Shape {
    constructor(renderer, radius) {
        super(renderer);
        this.radius = radius;
    }
    
    draw() {
        return this.renderer.renderCircle(this.radius);
    }
    
    resize(percentage) {
        this.radius *= percentage / 100;
        return this;
    }
}

class Square extends Shape {
    constructor(renderer, side) {
        super(renderer);
        this.side = side;
    }
    
    draw() {
        return this.renderer.renderSquare(this.side);
    }
    
    resize(percentage) {
        this.side *= percentage / 100;
        return this;
    }
}

With this pattern, we can:

Interactive Demo: Shape Rendering Bridge

Experiment with different shapes and rendering methods to see the Bridge pattern in action.

// Drawing instructions will appear here

When to Use

Benefits

Real-World Uses