I recently have been (re)learning about design patterns and had an assignment of looking into the Bridge Pattern. In fact, part of the assignment was coming up with a good way of describing the pattern. I believe I have done this and felt like I should post my findings here, if only for my future self.
One particular “textbook definition” of the Bridge Pattern (from SourceMaking) is as follows:
“Decouple an abstraction from its implementation so that the two can vary independently.”
If you’re like me, you’ll read this and go “huh?”, then proceed to stare blankly at the words looking for some meaning. While that SourceMaking page does a good job of explaining what Bridge Pattern is on a technical level, what I needed was a dumbed-down explanation and example.
An Internet search for “what is the point of the bridge pattern” brought me to such an explanation. It can be found here, and I highly recommend reading it (but not just yet).
After reading that article, I came up with my own definition of the Bridge Pattern as follows: The Bridge Pattern lets you have two sets of many classes and easily mix-and-match to have any combination of them. Not only are they easy to mix-and-match, but without the pattern, adding an item to either set of classes would mean adding multiple combined class implementations to your code. With the pattern, you only ever have to add the single class you need. This may still sound weird, but let me explain with an example.
Let’s say you have a program that deals with different beverages. There can be different be types of beverages (soda, beer, water, etc), and each can be contained within different types of containers (aluminum can, plastic bottle, glass bottle, etc). As you can see, we have two dimensions of “things”: types of beverages and types of containers. Think of these as lists, and any item in one list can be paired with any item in another list. E.g. there can be canned beer, bottled beer, canned soda, bottled soda, etc.
So, let’s say the containers are interfaces and the beverages are classes (implementations). In order to have all possible combinations of beverages and containers, we could manually pair them up and make the following classes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Hopefully you can see how quickly this can get really nasty. In this example, we’re only dealing with a 2x2 matrix: two containers (bottle and can) and two drinks (beer and soda). If we needed to add, say, water, we would have to add two new classes:
1 2 3 4 5 6 7 8 9
Then if we wanted to add glass bottles, we’d have to add three more:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Like I said, it gets nasty really quick. We just went from 4 concrete classes to 9 just to add two things (the glass interface and the water implementation). On top of that, we have quite a bit of duplicate code. See all the comments like
// do water stuff?
So here’s where the Bridge Pattern comes into play. Let’s use our original 2x2 example. First, we want to be able to treat every type of container the same (for polymorphism purposes, etc.) as well as have common functionality (like having a contained beverage):
1 2 3 4 5 6 7
Then we create individual classes for each of our containers, extending Container:
1 2 3 4 5 6 7 8 9 10 11 12 13
So now we have two Containers that will hold beverages, no matter what the beverage actually is. We’ve abstracted out the beverages, but we still need write them. We start with a common interface (which was used in our classes above):
1 2 3
then the classes:
1 2 3 4 5 6 7 8 9 10 11
Great! Now we have completely independent containers and beverages (although the containers do have a dependency on Beverage). How do we use it? Easy!
What if we want a plastic bottle of beer or soda instead?
Now comes the real fruit of your labor. Remember how before, when we added water, we had to add two classes? Well now we just have to add one!
1 2 3 4 5
Then we can get either a can or a plastic bottle of water:
Previously at this point, in order to add glass bottles for every type of beverage, we had to add three more classes. Now, we only have to add a single new class.
1 2 3 4 5 6
Getting a glass bottle of beer is no different than the others:
In total we now have the same functionality with 6 classes instead of 9 (if you don’t include the abstract class), and we only ever have to add a single class any time we add an item to either group (beverage or container).
Hopefully this clears it all up for you. Now that you’re through with my post, I recommend you finally go read the article I referenced before.