Last updated on 2014-09-07
The Bridge Design Pattern is a structural design pattern used to completely decouple an abstraction from it implementation, so that both of them can change independently. This may sound a bit strange because this is the reason interfaces exist – to decouple abstractions from implementations. But the Bridge DP goes a step further, not just separating them but also putting a river between them and building a bridge on top of the river :-). It also looks very similar to an Adapter, but the main difference is that the adapter is created AFTER you have an implementation and you want to change its interface, whether a bridge is created AT DESIGN time to decouple the system (and avoid the need to write adapters later on).
I have to tell you that I read a lot of material on the internet about this pattern before really getting it… and I am still not sure if I got it right. But let’s try modeling it. First, let’s look at the typical class diagram for the Bridge design pattern:
yUML code:
[Client]-->[Abstraction|operation()]
[Abstraction]<>-impl->[AbstractImplementor|operationImpl()]
[AbstractImplementor]^-[ConcreteImplementor1|operationImpl()]
[AbstractImplementor]^-[ConcreteImplementor2|operationImpl()]
Note that many class diagrams also show a RefinedAbstraction
class but never tell anything about it, making things even harder to understand. Furthermore, which class is the bridge? I would guess it is Abstraction
… so why no name it Bridge
? But most important, how is this bridge used? How much logic should be found in the Abstraction
? Should it simply forward requests or can it perform other stuff? I’m confused… OK, I think the best answer it the one given in this SO question (answer by thSoft) who says that bridge “is the solution whenever there are two orthogonal dimensions in the domain”.
Lets take a real-world example. I use the eclipse draw2d framework a lot and it is a good example of how to use a bridge. On the one hand you have an abstraction hierarchy: Figure
, with all of its children. On the other hand they can be drawn on many different platforms (SWT, AWT, GWT, etc). The dumb solution to implement this would be to inherit from the Figure
class for each possible drawing platform, but this would rapidly create an exploding class hierarchy. The solution used in draw2d is to create a Graphics
class that bridges between the figures and the platform. When a figure wants to draw itself, it uses the methods of Graphics
, which are then mapped to methods in the specific drawing platform. This is how it looks in a class diagram:
yUML code:
[Client]->[Figure|paint(Graphics)]
[Figure]^-[Rectangle|paint(Graphics)]
[Figure]^-[Ellipse|paint(Graphics)]
[Figure]->[Graphics]
[Graphics]^-[SWTGraphics]
While here the implementation is not a part of the abstraction (as shown in the “typical” bridge class diagram), the result is the same. We can change the Figure
hierarchy without affecting the Graphics
hierarchy and vice-versa.
So let’s see how this looks in a sequence diagram.
WebSequenceDiagrams code:
Client->Abstraction:operation()
Abstraction->Implementation:operationImpl()
Too simple and doesn’t tell the full story. One of the problems with sequence diagrams is that they don’t handle polymorphism very well. But let’s try again using alternative fragments to show how this would work:
WebSequenceDiagrams code:
note over Client,Abstraction
instantiate an abstraction
with the provided implementation
end note
Client-->Abstraction:new(impl)
Client->Abstraction:operation()
Abstraction->Implementation:impl.operationImpl()
note over Abstraction,Implementation:polymorphic call
alt impl instanceof Implementation1
Implementation->Implementation1:operationImpl()
Implementation1-->Abstraction:
else impl instanceof Implementation2
Implementation->Implementation2:operationImpl()
Implementation2-->Abstraction:
end
Abstraction-->Client:
This provides us with a lot more information, but borders on the limits of expressiveness and complexity that make sequence diagrams useful (and I had to add a number of comments so that it is understandable).
Personally, I learned a lot from this exercise, but the resulting sequence diagram is definitely not worth the effort. Maybe this is because this DP is fairly abstract. Or maybe because sequence diagrams are worthless. Or maybe I just don’t get it. But who cares.
Oh, and another thing: while I used draw2d as an example of the Bridge DB, its current implementation actually makes it pretty hard to use other drawing frameworks. This shows that you may have good intentions, but a bad implementation can destroy them very fast. But there is hope – the new version of draw2d has completely separated SWT from draw2d! I event managed to add GWT support with very small effort. This is a demonstration of good design.
Be First to Comment