Bridge pattern is part of the Structural design patterns. As the name may suggest that it acts as an intermediate between to different components. But if we talk about the Adapter pattern, then both patterns are having same logical definition. To say that it acts as an intermediate, is partially correct. Both are acting as a type of intermediate between two systems, but actual difference is the goal which these patterns achieve. Adapter pattern is aimed at acting an intermediate when the two components are not compatible to work with each other. Also adapter pattern can add more functionality to source component request, before passing it on to the target component, with which the source component want to interact. On the other hand, Bridge patterns’ aim is to provide a multiple path way between two components, which is nothing but achieving many to many communication between multiple implementations of the source and the target components. So technically, it receives request from one of the many implementations of the source component, and based on the client requirements, send it to one of the many implementations of the target component.
What is Bridge pattern ?
According to GoF’s definition, this pattern is :
Separates an object’s interface from its implementation
As per the above definition, it helps us to create a structure, where even the interface is separated from the implementation using a bridge. Technically this results in a system where a function can have multiple implementations and each implementation of that function can be used in multiple ways. So this results in a system with many to many mappings. Let’s discuss this using a real world example.
A real world example :
Before we start with example, let’s discuss the basic requirements we have. We have an abstract business logic to perform some operation (like saving to database or sending notifications to user) and this operation can be done in multiple ways.
Let’s take an example of a system which is capable of sending Emails & SMS’s using a web-service created by you. So what we will have in the code ? An interface which will be having a method declaration, to send the email & sms, implemented by the Email and the SMS concrete classes.
So in our example, sending the SMS and Email notifications to users is our business logic and sending it through Webservice, is one of the many different ways to achieve this task.
So our code will become something like the below :
Fine till now. Consider a case, where you are required to add the ability to send the Emails & SMS’s using a third party API. What to do now, you will introduce another interface, with 2 more implementation classes. So the number of classes increases. Another way to send SMS & Email using WCF service, comes into the picture and you again add one more type of interface and its 2 different implementations . So you can see here :
- Started with 1 interface and its 2 implementations – 3 Classes(including the interface to be precise). Then
- Added one more interface for third party API and its 2 implementations – 3 + 3 (from step 1) = 6 classes (including 2 interfaces). Then
- Added the third interface for sending using WCF service its 2 implementations – 3 +6 (step 1 & 2 classes).
The total reaches to 9 and this will keep on increasing, as the number of methods to implement the logical code keeps increasing. Further if more ways to send user notifications are added, they will be adding more classes to the system and it will become difficult to maintain the code and make changes in future.
This is where the Bridge pattern comes into the picture and provides us multiple ways to complete a task which is having multiple implementations. This is what we meant by many to many mappings at the start of the discussion. So let’s discuss how we can avoid this situation using this pattern.
To solve this problem, we introduce another level of abstraction. This time, its not for the logic implementation, but for the different ways in which we can call this logic. So we create an abstract bridge which will be used to link the two systems i.e. different ways to send different types of user notifications.
Our system code will be divided into the following 4 components :
1. Abstraction : This will be the abstract class which will be having the abstract logic to be implemented. Most importantly, this will hold reference to the bridge ( which will internally having reference to the system through which notification is to be sent). Rest is just like any other interface based definition of the functions to be implemented.
2. Abstraction Details : These will be concrete implementations of step 1 abstraction
3. Bridge Abstraction : This is the abstract component which will act as a bridge between the two components.
4. Bridge Implementations : These are the implementations for the bridge and will provide different ways in which we can call the required logic implementations of step 2.
So our first step will be to define the the bridge abstraction and the logic abstraction class. Our logic abstraction class will be having a reference to the abstract bridge. Next, we will implement the abstract logic and call the bridge method, using its reference, which these concrete logic based classes hold withing them. See the code below :
Next we will be having different implementations of the bridge, which is nothing but different ways in which we can send these user notifications. So this is nothing but another implementation of the interface, but this time its the Bridge interface.
Finally we will write the client code.
So run the code and see the results.
How it works ?
Now let’s see what actually is happening. We are setting the basic properties of our class SendData, like we would do for any other class. One of this property is the abstract bridge reference in the form of property named _iBridgeComponents. So this property is basically assigned the actual or the concrete bridge which we want to use for the logic processing. I first case, its the webservice and in second case its the third party API. So when we invoke the Send() function using this property, it is actually invoking the Send() function of the class for which it holds the reference. In first case this happens to be the webservice and in second case, its the third party API.
At the start, we discussed how the number of classes can increase without this pattern. Here we are having 6 classes (including the interface). Even if we add another method using WCFService, we will have only one more class added to the system, which makes it 7. So in future, even if we keep adding the logic implementations and the methods to achieve it, the number of classes does not increases, as it would have without this pattern. Hope this makes it clear.
So this was all about Bridge design pattern.