← Back to list

Building Flexible Systems — The Bridge Design Pattern

Ever worked on a feature where adding a new option suddenly meant changing a ton of existing code?

Dhanush Prabakaran · 2025-10-08 04:15 · 0 claps · 2.3 min read
#bridge-design-pattern #ascentware #developer-blog #clean-code #design-patterns
Open on Medium ↗

Building Flexible Systems — The Bridge Design Pattern

Ever worked on a feature where adding a new option suddenly meant changing a ton of existing code?

Maybe you were adding new devices that connect to different message channels (like email, SMS, or push notifications). Each new combination meant more classes, more duplication, and more confusion.

That’s exactly where the Bridge Design Pattern shines.

It helps you decouple abstraction from implementation so both can evolve independently — without breaking each other.

Without the Bridge Pattern

Let’s say you have two abstractions: Messages and Notification Channels.

You might start with something like this:

// Message types
public class TextMessage {
    public void send() {
        System.out.println("Sending TEXT message via Email");
    }
}

public class EmailMessage {
    public void send() {
        System.out.println("Sending EMAIL message via Email");
    }
}

Now your manager says, “We also need to send messages via SMS!” So you add more classes:

public class TextMessageSMS {
    public void send() {
        System.out.println("Sending TEXT message via SMS");
    }
}

public class EmailMessageSMS {
    public void send() {
        System.out.println("Sending EMAIL message via SMS");
    }
}

And soon…

  • Text + Email × Email + SMS = 4 classes
  • Add Push Notifications → 6 classes
  • Add WhatsApp → 8 classes

You can already feel the pain — the number of classes grows exponentially as features increase.

With the Bridge Pattern

Let’s fix it the right way. We’ll separate the two hierarchies:

  • The Abstraction (Message type)
  • The Implementation (Notification channel)

Then, we’ll use a bridge between them.

Step 1: Define the Implementor (Channel)

public interface MessageSender {
    void sendMessage(String message);
}

Step 2: Concrete Implementations

public class EmailSender implements MessageSender {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending via Email: " + message);
    }
}

public class SmsSender implements MessageSender {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending via SMS: " + message);
    }
}

Step 3: Abstraction (Message)

public abstract class Message {
    protected MessageSender sender;

    public Message(MessageSender sender) {
        this.sender = sender;
    }

    public abstract void send(String message);
}

Step 4: Refined Abstractions

public class TextMessage extends Message {
    public TextMessage(MessageSender sender) {
        super(sender);
    }

    @Override
    public void send(String message) {
        sender.sendMessage("Text: " + message);
    }
}

public class EmailMessage extends Message {
    public EmailMessage(MessageSender sender) {
        super(sender);
    }

    @Override
    public void send(String message) {
        sender.sendMessage("Email: " + message);
    }
}

Step 5: Use the Bridge

public class Main {
    public static void main(String[] args) {
        MessageSender email = new EmailSender();
        MessageSender sms = new SmsSender();

        Message textMsg = new TextMessage(sms);
        textMsg.send("Hello via SMS!");

        Message emailMsg = new EmailMessage(email);
        emailMsg.send("Hello via Email!");
    }
}

Now you can add new message types or new senders independently. No class explosion, no duplication, no mess.

Why the Bridge Pattern Rocks

  • Scalable — Add new features without touching old ones.
  • Decoupled — Abstraction and implementation evolve separately.
  • Clean Code — Fewer classes, less duplication, easier maintenance.

When to Use a Bridge

  • When you have multiple dimensions of variation (like message type × delivery channel).
  • When you want to avoid an explosion of subclasses.
  • When you need flexibility to mix and match features dynamically.

Do You Know?

You’ll find Bridge used in frameworks like JDBC (Driver + Database), UI themes, and logging systems where abstraction and implementation are loosely coupled.

Next time your classes start multiplying out of control, build a Bridge and keep your system clean and scalable.


메타데이터
post_id
cafe11e5ba3d
slug
building-flexible-systems-the-bridge-design-pattern-cafe11e5ba3d
url
https://medium.com/@dhanushprabakaran/building-flexible-systems-the-bridge-design-pattern-cafe11e5ba3d
canonical_url
https://medium.com/@dhanushprabakaran/building-flexible-systems-the-bridge-design-pattern-cafe11e5ba3d
author_url
https://medium.com/@dhanushprabakaran
status
ok
fetched_at
2026-08-04 02:06:22