It’s design pattern time again! This time, let’s talk about the bridge design pattern. The bridge pattern is officially meant to “decouple an abstraction from its implementation so that the two can vary independently” which is just all kinds of helpful. The design patterns book has a lot of great ideas but they’re not always communicated especially clearly. That definition of the bridge pattern sounds an awful lot like the adapter pattern, which is meant to “convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.”
First let’s talk about what the bridge pattern actually is and then we can get into how the bridge and adapter are different.
The way I would define the bridge pattern is it decouples multiple abstractions so they can both vary without making a huge mess. Maybe that’s clearer and maybe it’s not, so how about an example. I’m going to steal John Sonmez’s web app type and theme example from his post on the bridge pattern. Let’s imagine we have a web application framework that we can base different applications on, like a blog or a news site or a store. That’s one abstraction. Now let’s imagine we want to add themes. That’s another abstraction. When we first start building themes, it’s tempting to subclass our first abstraction, the web app type, for each theme. If we have two themes, say light and dark, we end up with six subclasses: blog-light, blog-dark, store-dark, store-light, news-light, and news-dark. That’s kind of a mess, and it’s only going to get worse when we add more themes and more app types.
What would be a lot cleaner is if we separated app type from theme so they can each vary without requiring an explosion of subclasses. If theme was separate from app type and each app type had a theme (composition over inheritance!), we could add all the themes we wanted without having to create any more app type subclasses.
Or to put it another way (ascii art diagram also by John Sonmez):
When:
A / \ Aa Ab / \ / \ Aa1 Aa2 Ab1 Ab2
Refactor to:
A N / \ / \ Aa(N) Ab(N) 1 2
Instead of having one complicated hierarchy, sometimes it’s easier just to have two simple hierarchies.
Hopefully the bridge pattern makes sense now. On to the adapter!
The adapter pattern has a really simple real-world analog -it’s the object equivalent of the power plug adapter you use when you travel to a country with different wall sockets and you want to be able to plug your laptop in.
For a more codey example, imagine you have an application that notifies the person on call when a status check fails or something weird happens in the log. Depending on how urgent the event is the app either sends an email, a text, or a phone call. The code that decides a notification should be sent shouldn’t know about the details of sending texts vs making phone calls, it should be able to give the notifier class a recipient and a message and be done with it. The problem is that the libraries used to send texts and phone calls and emails all have different interfaces and that makes our code a mess. To clean it up, we use the adapter pattern (also known as a wrapper) to make the interfaces to each of those libraries look the same. That lets us use each library without having a big ugly if statement with slightly different method calls for each type of notification we want to send.
The adapter and bridge patterns are pretty closely related and it’s not unusual to need both of them. In the web app type and theme example above we didn’t get too far into implementation details, but if we wanted to add a theme created by someone else we might need an adapter to fit the new theme into our existing bridge pattern.