Skip to contentJMRG
All posts

Bridge Pattern: Separating Abstraction from Implementation

design-patternsstructuralbridge

Intent

Bridge decouples an abstraction from its implementation, allowing both to vary independently. It avoids the combinatorial explosion of classes when you have multiple dimensions of variation.

The Problem

Imagine a notification system with multiple channels and message types:

// Without Bridge: class explosion
class EmailUrgentNotification { }
class EmailRegularNotification { }
class SMSUrgentNotification { }
// 2 types × 3 channels = 6 classes
// 4 types × 5 channels = 20 classes 😱

The Solution: Bridge

classDiagram
    class Notification {
        <<abstract>>
        #channel: NotificationChannel
        +send(message: string)
    }

    class NotificationChannel {
        <<interface>>
        +deliver(message: string, priority: string)
    }

    Notification o--> NotificationChannel

TypeScript Implementation

// Implementation (how to deliver)
interface NotificationChannel {
  deliver(message: string, priority: 'high' | 'normal'): Promise<void>;
}

class EmailChannel implements NotificationChannel {
  async deliver(message: string, priority: 'high' | 'normal'): Promise<void> {
    // Send via SMTP
  }
}

class SMSChannel implements NotificationChannel {
  async deliver(message: string, priority: 'high' | 'normal'): Promise<void> {
    // Send via Twilio
  }
}

// Abstraction (what to send)
abstract class Notification {
  constructor(protected channel: NotificationChannel) {}
  abstract send(message: string): Promise<void>;
}

class UrgentNotification extends Notification {
  async send(message: string): Promise<void> {
    await this.channel.deliver(`🚨 URGENT: ${message}`, 'high');
  }
}

// Usage: Combine any type with any channel
const urgentEmail = new UrgentNotification(new EmailChannel());
const urgentSMS = new UrgentNotification(new SMSChannel());

When to Use

Situation Recommendation
Multiple dimensions of variation ✅ Recommended
Avoid subclass explosion ✅ Recommended
Change implementation at runtime ✅ Recommended
Single dimension of variation ❌ Simple inheritance

Conclusion

Bridge is essential when you have two orthogonal axes of variation and want to combine them freely.