RO EN

Bridge Pattern – separating abstraction from implementation

Bridge Pattern – separating abstraction from implementation
Doru Bulubasa
19 November 2025

In software architecture, as projects grow, there is often a need to combine multiple types of abstractions with multiple types of implementations. If you're not careful, you quickly end up with a class explosion – dozens of possible combinations between functionalities, each requiring its own derived class.

This is where the Bridge Pattern comes into play, a structural pattern that clearly separates abstraction from implementation, allowing them to evolve independently.


🧩 What is Bridge Pattern?

Bridge Pattern provides us a way to connect two different class hierarchies:

  • one for abstraction (usually high-level logic)

  • one for implementation (concrete details)

Instead of having a single large hierarchy, you separate them, and communication between them is done through a bridge, represented by an interface or an abstract class.

Thus you have:

Abstraction ---> Implementation


🧠 Why is Bridge Pattern useful?

  • ✔ Avoids subclass explosion

  • ✔ Allows adding new implementations without modifying the abstraction

  • ✔ Provides flexibility in configuration and extension

  • ✔ It is excellent when working with multiple platforms, drivers, services, or devices


📦 Practical example (C# / .NET)

Let's say we have a system that sends notifications. Notifications can be:

  • Email

  • SMS

And the notification levels can be:

  • Simple

  • Important

Without Bridge Pattern, we would have classes like:

  • SimpleEmailNotification

  • ImportantEmailNotification

  • SimpleSmsNotification

  • ImportantSmsNotification

    …and it becomes hard to scale.

With Bridge Pattern: we separate what we send from how we send it.


The implementation (the bridge):

public interface INotificationSender
{
    void Send(string message);
}

public class EmailSender : INotificationSender
{
    public void Send(string message)
    {
        Console.WriteLine($"Email sent: {message}");
    }
}

public class SmsSender : INotificationSender
{
    public void Send(string message)
    {
        Console.WriteLine($"SMS sent: {message}");
    }
}


The abstraction (logic level):

public abstract class Notification
{
    protected readonly INotificationSender _sender;

    protected Notification(INotificationSender sender)
    {
        _sender = sender;
    }

    public abstract void Notify(string message);
}


Versions of the abstraction:

public class SimpleNotification : Notification
{
    public SimpleNotification(INotificationSender sender) : base(sender) { }

    public override void Notify(string message)
    {
        _sender.Send("[Simple] " + message);
    }
}

public class ImportantNotification : Notification
{
    public ImportantNotification(INotificationSender sender) : base(sender) { }

    public override void Notify(string message)
    {
        _sender.Send("[IMPORTANT] " + message);
    }
}


Usage:

var emailSender = new EmailSender();
var smsSender = new SmsSender();

Notification n1 = new SimpleNotification(emailSender);
n1.Notify("Hello!");

Notification n2 = new ImportantNotification(smsSender);
n2.Notify("You have an important message!");


🏁 Conclusion

Bridge Pattern is excellent for situations where you need flexibility between something abstract and the way it is implemented. Instead of creating a single large class hierarchy, you separate them into two independent hierarchies, connected through an interface (bridge).

It is ideal for:

  • cross-platform systems

  • pluggable modules

  • high extensibility scenarios

  • complex enterprise architectures