Bridge Design Pattern is used to decouples an abstraction used the client code from its implementation that means it separates the abstraction and its implementation in separate class hierarchies. And also Bridge pattern prefers the composition over the inheritance because inheritance isn’t always flexible and it breaks the encapsulation, so any change made in the implementor that affects the abstraction used by client code.
In software engineering, one of popular notion is “prefer composition over inheritance“. Bridge design pattern promotes this popular notion. Similar to the other pattern, this pattern also comes under the structural design pattern family of GoF Design pattern.
Bridge Design Pattern
According to the Gang of Four:
Decouple an abstraction from its implementation so that the two can vary independently.
Bridge pattern uses an interface as bridge between the concrete classes of an abstract class and implementing classes of that interface. You can make changes in both types of classes without any impact on the client code.
Spring 5 Design Pattern Book
Applicability
There are following common problems solved by the Bridge Design Pattern.
- Remove a permanent binding between the functional abstraction and its implementation.
- You able to make changes into implementing classes without affecting the its abstraction and client code.
- You can extends the abstraction and its implementation using sub classes.
also read:
UML class diagram of the Bridge Pattern
Let’s see the following class diagram. It illustrates about the component classes and interfaces.
The classes and objects participating in this pattern are:
Abstraction (Bank)
- It defines the abstraction’s interface.
- It maintains a reference to an object of type Implementor.
RefinedAbstraction (IciciBank, HdfcBank)
- It extends the interface defined by Abstraction.
Implementor (Account)
- It defines the interface for implementation classes. This interface doesn’t have to correspond exactly to Abstraction’s interface; in fact the two interfaces can be quite different. Typically the Implementation interface provides only primitive operations, and Abstraction defines higher-level operations based on these primitives.
ConcreteImplementor (CurrentAccount, SavingAccount)
- It implements the Implementor interface and defines its concrete implementation.
Pros of the Bridge Pattern
There are following pros of the Bridge Design Pattern.
- This design pattern allows you to make separation between the implementation and the abstraction.
- Bridge design pattern provides flexibility to change in both types of classes without side effect into client code.
- This pattern allows the hiding of actual implementation details from the client by using abstraction between them.
Bridge Vs Adapter Design Pattern
The adapter design pattern helps it two incompatible classes to work together. But, bridge design pattern decouples the abstraction and implementation by creating two different hierarchies.
Sample Implementation of Bridge Design Pattern
You look in the following example where I am going to demonstrate use of Bridge design pattern. Here I am taking two accounts of any Bank, you can open two types of accounts one is SavingAccount and other is CurrentAccount. Let’s see the relationship between the abstraction of a Bank and Account.
Relationship without using Bridge Design Pattern
Let’s start to create a design without using Bridge Design Pattern. First create an interface or an abstract class, Bank. And then I create its derived classes: IciciBank and HdfcBank. To open an account in bank, first decide type of accounts classes: SavingAccount and CurrentAccount, these classes extends the specific banks classes (HdfcBank and IciciBank). This simple deep inheritance hierarchy in this application.
So what is wrong with this design as below figure. You will notice in this design, there are two parts, one is the abstraction part and other is the implementation part. Client code interacts with the abstraction part. Client code can only access new change or new functionality of the implementation part when you will update the abstraction part, it means both parts the abstraction and implementation are tightly coupled with each-other.
Relationship using Bridge Design Pattern
Let’s look into the following figure, how Bridge design pattern solve these design issues as noticed in the without using bridge pattern example. Bridge pattern is separating the abstraction and implementation into two class hierarchies.
We have a Account interface which is acting as a bridge implementer and concrete classes SavingAccount, CurrentAccount implementing the Account interface. Bank is an abstract class and will use object of Account.
Step 1: Let’s create bridge implementer interface.
Account.java
package com.doj.patterns.structural.bridge; /** * @author Dinesh.Rajput * Implementor for bridge pattern */ public interface Account { Account openAccount(); void accountType(); }
Step 2: Let’s create concrete bridge implementer classes implementing the Account interface.
CurrentAccount.java
package com.doj.patterns.structural.bridge; /** * @author Dinesh.Rajput * Concrete implementation 1 for bridge pattern */ public class CurrentAccount implements Account { @Override public Account openAccount() { System.out.println("OPENED: CURRENT ACCOUNT "); return new CurrentAccount(); } @Override public void accountType() { System.out.println("##It is a CURRENT Account##"); } }
SavingAccount.java
package com.doj.patterns.structural.bridge; /** * @author Dinesh.Rajput * Concrete implementation 2 for bridge pattern */ public class SavingAccount implements Account { @Override public Account openAccount() { System.out.println("OPENED: SAVING ACCOUNT "); return new SavingAccount(); } @Override public void accountType() { System.out.println("##It is a SAVING Account##"); } }
Step 3: Let’s create an abstract class Bank using the Account interface.
Bank.java
package com.doj.patterns.structural.bridge; /** * @author Dinesh.Rajput * Abstraction in bridge pattern */ public abstract class Bank { //Composition with implementor protected Account account; public Bank(Account account){ this.account = account; } abstract Account openAccount(); }
Step 4: Let’s create concrete class implementing the Bank interface.
IciciBank.java
package com.doj.patterns.structural.bridge; /** * @author Dinesh.Rajput * Refine abstraction 1 in bridge pattern */ public class IciciBank extends Bank { public IciciBank(Account account) { super(account); } @Override Account openAccount() { System.out.print("Open your account with ICICI Bank"); return account; } }
HdfcBank.java
package com.doj.patterns.structural.bridge; /** * @author Dinesh.Rajput * Refine abstraction 2 in bridge pattern */ public class HdfcBank extends Bank { public HdfcBank(Account account) { super(account); } @Override Account openAccount() { System.out.print("Open your account with HDFC Bank"); return account; } }
Step 5: Let’s create a demo class and use the Bank and Account classes to open different types of accounts Saving and Current Account.
BridgePatternMain.java
package com.doj.patterns.structural.bridge; /** * @author Dinesh.Rajput * Demonstration of bridge design pattern */ public class BridgePatternMain { public static void main(String[] args) { Bank icici = new IciciBank(new CurrentAccount()); Account current = icici.openAccount(); current.accountType(); Bank hdfc = new HdfcBank(new SavingAccount()); Account saving = hdfc.openAccount(); saving.accountType(); } }
Step 6: Let’s run the above demo class and verify the output.
Open your account with ICICI Bank##It is a CURRENT Account## Open your account with HDFC Bank##It is a SAVING Account##