Introduction
In this series of learning different design patterns, we came across a new and the most famous pattern, the bridge design pattern. As discussed in the last article, we saw what a design pattern is, and what it provides when implemented with the builder design pattern.
A design pattern provides a simple solution to common problems that are faced in day-to-day life by software developers. Three basic types of design patterns exist, which are listed below:
- Creational Pattern
- Structural Pattern
- Behavioral Pattern
Thus, the pattern described here is like a blueprint that you can customize to solve a particular design problem and they only differ by complexity and scalability of the code.
In this article, we will learn about the most famous and commonly used design pattern i.e bridge design pattern. Let’s get started.
Why the Bridge Design Pattern?
To start with, what is a bridge design pattern? One should know why we need this design pattern, what are the drawbacks behind the other patterns to need to introduce a new design pattern, and why we need this new pattern in our code.
In the native language, this pattern comes into use to separate abstraction from its implementation so that it can be modified independently, i.e. changes in their implementation should not have an impact on the entire program (recompiled).
We choose the Bridge Design Pattern when.
- We need to avoid a permanent binding between an abstraction and its implementation i.e to avoid tight coupling between interface/abstract class and other classes.
- Both abstraction and their implementation should be extensible by subclassing.
- When changing in their implementation should not have an impact on the customer/client i.e their code should not have to be recompiled.
- When we need to share an implementation among multiple objects, and
- When we need to hide the implementation of an abstraction completely from clients.
To overcome this problem, we need to find a new design pattern.
What is the Bridge Design Pattern?
Bridge Design Pattern is a part of a structural design pattern, as the name suggests it acts as an intermediary between two components.
As per the “Gang of Four” definition, the Bridge Design Pattern decouples an abstraction from its implementation so that the two can vary independently which means, this pattern is used to separate abstraction from its implementation so that can be modified independently.
More specifically this pattern involves an interface that acts as a bridge between the abstraction classes and implementer classes. With the bridge pattern, both types of classes can be modified without affecting each other.
Implementation
One thing that comes to mind when we listen to the word “bridge” i.e pillars, and the same applies to this design pattern, i.e. we have an implementer and that implementer has concrete implementers for the given interface that, act as pillars (concrete implementer) to the bridge (implementer).
Let’s assume that our requirement is to process the payment of the customer who had either opted for an option of Credit/Debit card or NetBanking or Pay on the Delivery during its purchase process.
Typically, we are provided with NetBanking, Credit/Debit cards, and Pay on the Delivery process as payment choices to do the payments.
Step 1. Construct an implementer i.e. “payment-system” which has a method to process the payments.
package BridgeDesign;
//Interface
public interface PaymentSystem {
//Method
void ProcessPayment();
void ProcessPayment(String string);
}
Step 2. In order to process the payment, there are many payment gateways like SBIpaymentSystem, PNBpaymentSystem, and IDBIpaymentSystem, to construct classes for the same.
SBIpaymentSystem class
package BridgeDesign;
public class SBIpaymentSystem implements paymentSystem {
@Override
public void ProcessPayment(String string) {
// TODO Auto-generated method stub
System.out.println("Using SBI payment gateway for " + string);
}
@Override
public void ProcessPayment() {
// TODO Auto-generated method stub
System.out.println("User will Pay on Delivery");
}
}
PNBpaymentSystem class
package BridgeDesign;
public class PNBpaymentSystem implements paymentSystem {
@Override
public void ProcessPayment(String string) {
// TODO Auto-generated method stub
System.out.println("Using PNB payment gateway for " + string);
}
@Override
public void ProcessPayment() {
// TODO Auto-generated method stub
System.out.println("User will Pay on Delivery");
}
}
Similarly, for other banks too we will be constructing the same classes. To simplify it, we are just returning the System.out.println line.
Now, we constructed the implementer interface as well as concrete implementers for the given interface.
Step 3. Construct a new abstract class, i.e. payment which had a method to make payment.
package BridgeDesign;
//Abstraction
public abstract class Payment {
paymentSystem payment; //instance
public abstract void makePayment(); //method responsible to makePayment
}
Step 4. Construct those refined abstractions to implement this make payment () method in the Payment abstract class.
For credit card
package BridgeDesign;
//Refined Abstraction
public class CreditCardPayment extends Payment {
@Override
public void makePayment() {
//payment object provides independency
payment.ProcessPayment("Credit Card Payment");
}
}
For debit card
package BridgeDesign;
//Refined Abstraction
public class DebitCardPayment extends Payment {
@Override
public void makePayment() {
//payment object provides independency
payment.ProcessPayment("Debit Card Payment");
}
}
For net banking
package BridgeDesign;
//Refined Abstraction
public class NetBanking extends Payment {
@Override
public void makePayment() {
//payment object provides independency
payment.ProcessPayment("Net Banking");
}
}
Note. Notice that we are able to bridge the interface and decouple the abstraction implementations.
Step 5. Now switch to the main class.
package BridgeDesign;
public class PaymentProcessor {
public static void main(String[] args) {
// Using PNB payment gateway for Credit Card Payment
Payment order1 = new CreditCardPayment();
order1.payment = new PNBpaymentSystem();
order1.makePayment();
// Using PNB payment gateway for Debit Card Payment
Payment order12 = new DebitCardPayment();
order12.payment = new PNBpaymentSystem();
order12.makePayment();
// Using PNB payment gateway for Net Banking
Payment order11 = new NetBanking();
order11.payment = new PNBpaymentSystem();
order11.makePayment();
// User will Pay on Delivery
Payment order111 = new PayOnDelivery();
order111.payment = new PNBpaymentSystem(); // Pay on Delivery is irrespective of banks
order111.makePayment();
// Using same payment gateway with a different bank
// Using SBI payment gateway for Credit Card Payment
Payment order2 = new CreditCardPayment();
order2.payment = new SBIpaymentSystem();
order2.makePayment();
// Using SBI payment gateway for Debit Card Payment
Payment order21 = new DebitCardPayment();
order21.payment = new SBIpaymentSystem();
order21.makePayment();
// Using SBI payment gateway for Net Banking
Payment order22 = new NetBanking();
order22.payment = new SBIpaymentSystem();
order22.makePayment();
// User will Pay on Delivery
Payment order222 = new PayOnDelivery();
order222.payment = new SBIpaymentSystem(); // Pay on Delivery is irrespective of banks
order222.makePayment();
}
}
Output
Using PNB payment gateway for Credit Card Payment
Using PNB payment gateway for Debit Card Payment
Using PNB payment gateway for Net Banking
Users will Pay on Delivery
Using SBI payment gateway for Credit Card Payment
Using SBI payment gateway for Debit Card Payment
Using SBI payment gateway for Net Banking
Users will Pay on Delivery
Similarly, we can access the payment process with different banks based on availability.
I hope it will make some sense to understand what the Bridge Pattern is. With this, we successfully implemented the Bridge Design Pattern.
Summary
Coming towards the end of this article, we learned how to create a builder design pattern.
What did we learn?
- What is a Design Pattern?
- Why the Bridge Design Pattern?
- What is a Bridge Design Pattern?
- Demo implementation