Bridge Design Pattern in Java

This article explains Bridge design pattern in java with UML class diagram. It then takes an example scenario in java and explains it with class diagram and code.
Introduction Bridge Design Pattern is a structural design pattern among the Gang Of Four(GOF) Design PatternsClick to read: Gang Of Four(GOF) Design Patterns. Being a structural design pattern, the Bridge pattern deals with how classes are composed together - here, by splitting one class hierarchy into two which are connected by composition. What is Bridge Design Pattern The Bridge design pattern decouples an abstraction from its implementation so that the two can vary independently. Suppose a class has two independent dimensions of variation - for example, the type of a notification (alert, reminder) and the channel it is sent over (email, SMS). Modelling both with inheritance leads to a subclass for every combination: EmailAlert, SmsAlert, EmailReminder, SmsReminder... and adding one channel means adding a class for every type. The Bridge pattern puts each dimension in its own hierarchy, and gives the abstraction a reference to an implementation object - the "bridge" - so that any abstraction can be combined with any implementation at runtime. Scenarios in which Bridge Design Pattern can be used
  1. Two independent dimensions of variation: When both the abstraction and its implementation should be extensible by subclassing, and combining them through inheritance would multiply the number of classes.
  2. Implementation chosen or switched at runtime: When the implementation should be selected by configuration, or changed while the program runs, rather than fixed at compile time.
  3. Implementation details hidden from clients: When changes to the implementation - a new email provider, a different SMS gateway - should not require recompiling or changing the client code.
Class Diagram for Bridge Design Pattern
Bridge Design Pattern Class Diagram
Explanation of Bridge Design Pattern's Class Diagram
  • Abstraction defines the high-level interface used by the client, and holds a reference to an Implementor - this reference is the bridge.
  • RefinedAbstractionA and RefinedAbstractionB extend the abstraction with variations of the high-level behaviour.
  • Implementor defines the low-level, primitive operations the abstraction is built on. It need not match the abstraction's interface.
  • ConcreteImplementorA and ConcreteImplementorB implement those operations in different ways, for example for different platforms.
  • Client works with the abstraction, which forwards the low-level work to its implementor. Either side can gain new subclasses without affecting the other.
Bridge Design Pattern in Java - Class Diagram
Bridge Design Pattern in Java Class Diagram
The example sends notifications. The abstraction side defines what a notification is - an alert or a reminder - and the implementation side defines how a message is delivered - by email or SMS.
Code for the classes shown in Java Example's Class Diagram
MessageSender.java - the implementor and its two implementations
//MessageSender.java
public interface MessageSender {
 void deliver(String message);
}
//EmailSender.java
class EmailSender implements MessageSender {
 public void deliver(String message) { System.out.println("  [email] " + message); }
}
//SmsSender.java
class SmsSender implements MessageSender {
 public void deliver(String message) {
  String sms = message.length() > 40 ? message.substring(0, 37) + "..." : message;   // SMS is short
  System.out.println("  [sms]   " + sms);
 }
}
Notification.java - the abstraction and its refinements
//Notification.java
public abstract class Notification {
 protected final MessageSender sender;          // the bridge to the implementation

 protected Notification(MessageSender sender) {
  this.sender = sender;
 }

 public abstract void notifyUser(String text);
}
//AlertNotification.java
class AlertNotification extends Notification {
 AlertNotification(MessageSender sender) { super(sender); }
 public void notifyUser(String text) {
  sender.deliver("ALERT! " + text.toUpperCase());
 }
}
//ReminderNotification.java
class ReminderNotification extends Notification {
 ReminderNotification(MessageSender sender) { super(sender); }
 public void notifyUser(String text) {
  sender.deliver("Friendly reminder: " + text);
 }
}
NotificationService.java (with the main() method)
//NotificationService.java
import java.util.List;

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

  // Any abstraction combines with any implementation: 2 + 2 classes give 4 combinations
  List<Notification> notifications = List.of(
    new AlertNotification(email),
    new AlertNotification(sms),
    new ReminderNotification(email),
    new ReminderNotification(sms));

  for (Notification n : notifications) {
   System.out.println(n.getClass().getSimpleName() + " via " + n.sender.getClass().getSimpleName() + ":");
   n.notifyUser("your invoice for October is due on 31st October 2026");
  }
 }
}
 OUTPUT on running NotificationService.java
AlertNotification via EmailSender:
  [email] ALERT! YOUR INVOICE FOR OCTOBER IS DUE ON 31ST OCTOBER 2026
AlertNotification via SmsSender:
  [sms]   ALERT! YOUR INVOICE FOR OCTOBER IS DU...
ReminderNotification via EmailSender:
  [email] Friendly reminder: your invoice for October is due on 31st October 2026
ReminderNotification via SmsSender:
  [sms]   Friendly reminder: your invoice for O...
Explanation of Java Example's Class Diagram & Code
  • Notification is the abstraction. It holds a MessageSender - the bridge - which it receives in its constructor.
  • AlertNotification and ReminderNotification are the refined abstractions. They decide what the message says, and call sender.deliver() without knowing how it is delivered.
  • MessageSender is the implementor, and EmailSender and SmsSender are the concrete implementors. They decide how a message is delivered - the SMS sender, for instance, shortens long messages.
  • NotificationService, the client, combines both kinds of alert with both channels - four combinations from four classes. With inheritance alone, it would take four classes for the combinations, and every new channel would add two more; with Bridge, a new channel - say, a push notification sender - is one new class which works with every type of notification.
Note - Bridge and AdapterClick to read: Adapter both put an object in front of another, but for different reasons. An Adapter makes an existing, incompatible class fit an interface after the fact; a Bridge is designed up front so that two hierarchies can grow independently. JDBC is a well-known bridge: application code uses the JDBC API (the abstraction), and each database driver provides the implementation.
Summary In the above tutorial we understood what is Bridge design pattern and the main scenarios in which it is applicable. We then looked at the UML class diagram for Bridge Design Pattern & its explanation, a Java use case implementing the Bridge pattern for notifications and delivery channels with its class diagram, code and output, followed by an explanation of both the class diagram & code. This concludes the tutorial on Bridge design pattern.