Bridge Pattern

Bridge Pattern

Decouple an abstraction from its implementation so that the two can vary independently.

Design Patterns: Elements of Reusable Object-Oriented Software

The Bridge Pattern: Introduction

We use abstraction to decouple client code from implementations, and the usual way is to use inheritance. We define an interface or an abstract class and create inheritance hierarchies from it, one for each of the several possible implementations. Although at first look this approach appears logical and nothing wrong in it, abstractions through inheritance isn’t always flexible. When we use inheritance, we are permanently binding the implementation to the abstraction. As a result, any change made to one affects the other. A more flexible way is to separate the abstraction and the implementation, and this is where the bridge pattern comes in.

Similar to the other patterns of the classic Gang of Four structural pattern family, the objective of the bridge pattern is to identify how to realize relationships between classes and objects in a simple way. The bridge pattern does it by separating the abstraction and the implementation in separate class hierarchies. The bridge between the class hierarchies is achieved through composition.

Participants of the Bridge Pattern

To understand how the bridge pattern works, consider a messaging application that clients can use to send different types of messages, such as a text or an email message. The most intuitive approach is to first create an interface or an abstract base class, Message. Next, we create the derived classes: TextMessage and EmailMessage. Finally, to send messages, we create two message sender classes: TextMessageSender that extends TextMessage and EmailMessageSender that extends EmailMessage. This is how our inheritance hierarchy looks like.
Bridge01

At first sight, there appears nothing wrong in the design above. But if you look deep you will notice that the abstraction part- the part that clients interact with, and the implementation part- the part that provides the core functionality of sending messages, are tightly integrated. Our design relies on inheritance and one inherent disadvantage is that it breaks encapsulation. As a developer of the EmailMessageSender subclass, you have to know about the internals of the EmailMessage superclass, which means the encapsulation in the superclass is broken.

Our design is also fragile. As an example, if we change the implementation to allow clients to optionally encrypt the message before sending, we will need to update the abstraction part to make the encryption functionality available to clients.

Another issue is reusability. If we want to reuse only the implementation (message sending) part in some other application, we will have to take along the abstraction part as extra baggage.

The bridge pattern addresses all such issues by separating the abstraction and implementation into two class hierarchies. This figure shows the design without and with the bridge pattern.

Class Hierarchies Without and With Bridge Pattern

With the bridge pattern, the abstraction maintains a Has-A relationship with the implementation instead of a IS-A relationship. The Has-A relationship is achieved through composition where the abstraction maintains a reference of the implementation and forwards client requests to it.

Let’s summarize the participants of the bridge pattern in the context of the messaging example:

  • Abstraction (Message): Is the interface implemented as an abstract class that clients communicate with.
  • RefinedAbstraction (TextMessage and EmailMessage): Are classes that implement or extend Abstraction.
  • Implementor (MessageSender): Is the interface of the implementation class hierarchy.
  • ConcreteImplementor(TextMessageSender and EmailMessageSender): Are concrete subclasses that implements Implementor.

Applying the Bridge Pattern

To apply the bridge pattern to the messaging example, let’s write the abstraction class hierarchy.

Message.java

package guru.springframework.gof.bridge.abstraction;

import guru.springframework.gof.bridge.implementation.MessageSender;

public abstract class Message {
    MessageSender messageSender;
    public Message(MessageSender messageSender){
        this.messageSender=messageSender;
    }
     abstract public void send();
}

TextMessage.java

package guru.springframework.gof.bridge.abstraction;

import guru.springframework.gof.bridge.implementation.MessageSender;

public class TextMessage extends Message{

    public TextMessage(MessageSender messageSender){
        super(messageSender);
    }
    @Override
    public void send(){
      messageSender.sendMessage();
    }

}

 

EmailMessage.java

package guru.springframework.gof.bridge.abstraction;
import guru.springframework.gof.bridge.implementation.MessageSender;

public class EmailMessage extends Message{
    public EmailMessage(MessageSender messageSender){
        super(messageSender);
    }
    @Override
    public void send(){
        messageSender.sendMessage();
    }

}

In the examples above, we first wrote the Message abstract class- the Abstraction. In Line 6 – Line 9 of the Message class, we initialized a reference to MessageSender in the class constructor. This is where we established a bridge between the two class hierarchies. We also declared an abstract send() message in Message. Next, we wrote two RefinedAbstraction: TextMessage, and EmailMessage to extend from Message. In the constructors of both the classes, we made calls through super() to the constructor of the parent Message class. In the overridden send() method, we invoked the sendMessage() method of MessageSender. Now lets write the implementation class hierarchy.

MessageSender.java

package guru.springframework.gof.bridge.implementation;

public interface MessageSender {
    public void sendMessage();
}

TextMessageSender.java

package guru.springframework.gof.bridge.implementation;


public class TextMessageSender implements MessageSender {
    @Override
    public void sendMessage(){
        System.out.println("TextMessageSender: Sending text message...");
    }
}

EmailMessageSender.java

package guru.springframework.gof.bridge.implementation;

public class EmailMessageSender implements MessageSender{
    @Override
    public void sendMessage(){
        System.out.println("EmailMessageSender: Sending email message...");
    }
}

In the MessageSender interface (Implementor), we declared a sendMessage() method that both the TextMessageSender and EmailMessageSender (ConcreteImplementor) classes override. Observe that this class hierarchy is completely independent of the abstraction class hierarchy that we wrote earlier. We can now modify, extend, and reuse one class hierarchy without worrying about the structure and participants of the other class hierarchy.

Here is a unit test for our bridge pattern example.

MessageTest.java

package guru.springframework.gof.bridge.abstraction;

import guru.springframework.gof.bridge.implementation.EmailMessageSender;
import guru.springframework.gof.bridge.implementation.MessageSender;
import guru.springframework.gof.bridge.implementation.TextMessageSender;
import org.junit.Test;

import static org.junit.Assert.*;


public class MessageTest {

    @Test
    public void testSend() throws Exception {
      MessageSender textMessageSender=new TextMessageSender();
      Message textMessage=new TextMessage(textMessageSender);
      textMessage.send();

       MessageSender emailMessageSender=new EmailMessageSender();
       Message emailMessage=new TextMessage(emailMessageSender);
       emailMessage.send();
    }
}

When you run the code above, you will see this output:

 
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running guru.springframework.gof.bridge.abstraction.MessageTest
TextMessageSender: Sending text message...
EmailMessageSender: Sending email message...
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 sec - in guru.springframework.gof.bridge.abstraction.MessageTest

Conclusion

The bridge pattern looks a lot like the adapter pattern and is a common cause of confusion. However, while the adapter pattern helps two incompatible interfaces work together, the bridge pattern helps decouple the abstraction and implementation by creating two separate class hierarchies. Also, as stated by GOF “Adapter makes things work after they’re designed; Bridge makes them work before they are.“.

So, when doing Enterprise Application Development with the Spring Framework, if you think about creating deep class hierarchies for different implementations, consider separating the low-level implementations into a separate class hierarchy connected with a bridge. Doing so will make your code more flexible and less fragile to changes. Also, with the bridge pattern in place, it will be much easier to unit test your code. Without the bridge pattern, in order to test implementation classes, you need their super classes. With the bridge pattern, you can test them independently, and then create mock objects of the implementation classes to test the refined abstraction classes.

17 comments on “Bridge Pattern

  1. July 18, 2016 at 8:33 pm

    Excellent Explanation!

    Reply
  2. September 12, 2016 at 3:07 pm

    “Our design is also fragile. As an example, if we change the implementation to allow clients to optionally encrypt message before sending, we will need to update the abstraction part to make the encryption functionality available to clients.”

    Can you explain how Bridge Pattern resolves this issue in your example? Aren’t you still going to have to make changes to MessageSender’s to accommodate for this feature?

    Reply
    • February 5, 2017 at 7:02 am

      That’s a great question. I also have the same concern as MalvoxW, I’m wondering how Bridge Pattern can resolves this issue, could you please help to explain clearer in your example?

      Reply
      • February 6, 2017 at 8:45 pm

        @MlvoxW @Tuan You can plug in EncryptedTextMessageSender and EncryptedEmailMessageSender implementations inheriting MessageSender. That should be all.

        Reply
        • October 17, 2021 at 8:04 pm

          I feel like the approach creates a problem similar to the one we were solving. I think it might be a better idea to handle any encryption by concrete instances of an encryption interface, i.e.

          IEncryptor, and then TextMessageEncryptor and EmailMessageEncryptor can implement that interface. And you can pass the relevant one as a dependency to your TextMessageSender or EmailMessageSender.

          Reply
  3. March 11, 2017 at 1:34 am

    Great article !

    One thing is not clear to me like i.e. MessageSender has a Message rather than Message has a MessageSender

    Reply
  4. April 20, 2018 at 4:37 am

    the best explanation for Bridge pattern, thanks

    Reply
  5. March 15, 2019 at 6:08 am

    I gone though many explanations on web for bridge design pattern but this one here is pretty simple and well formatted to understand. Thanks alot for such a nice article.
    Also, I have one doubt in example, why not MessageSender class has Message Object instead Message has MessageSender here.
    As MessageSender is sending Message, so it should have Message Object.Please correct me If I am wrong.

    Reply
  6. April 20, 2019 at 1:21 pm

    Very Good Explanation. Apt and Real World example are easy to follow. No Bookish material, top class.

    Reply
  7. October 15, 2019 at 4:35 am

    Shouldn’t message sender has a reference to message ?

    Reply
  8. May 18, 2020 at 12:34 pm

    Realy your the first I found who tackle design patterns in a serious manner, thank you. If I want to add something to your great explanation, I will add: Bridge hide implementation from Clients. Bridge let us vary implementation now and extend it in the future without changing the abstraction

    Reply
  9. June 9, 2020 at 3:19 am

    For me this example looks more like the strategy pattern (which is a behavior pattern) as the senders changes the behavior of the message. The message sender represents an algorithm, that can be exchanged for the corresponding message object at runtime.

    Reply
  10. August 8, 2020 at 3:53 pm

    Thanks for this. I too had come across many waffly and unhelpful attempted expositions of what the bridge pattern is before coming here.
    In a nutshell the pattern appears to be an example of the maxim “prefer composition over inheritance”. I’m wondering how it differs from mixins in that respect, if at all. My understanding of mixins is that they add functionality by using composition rather than extension/inheritance.

    Reply
  11. December 31, 2020 at 4:15 pm

    Nice Job! I have a question: In MessageTest.java line 20, EmailMessageSender will send a text message. Is that ok? So a TextMessageSender can send a HTML email message as well?

    Reply
  12. September 8, 2021 at 10:19 am

    I don’t think it is a good demo to show the bridge design pattern. The TextMessage should always has the TextMessageSender instead of EmailMessageSender, other it causes error.

    Reply
  13. March 4, 2024 at 9:46 am

    Adapter and Bridge patterns indeed look similar but the subtle difference between these two patterns is very well illustrated! thank you so much

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.