In this article we will look into Adapter Design Pattern in java and try to understand what problem it solves and its practical use case.
Adapter design pattern is one of the structural design pattern and is used to make two unrelated interfaces work together. The object that joins these unrelated interfaces is called an Adapter.
Introduction
Adapter design acts as a bridge between two interfaces, making them compatible and enabling them to work seamlessly. This pattern is useful when you need to integrate existing code or libraries that have different interfaces with your codebase without making significant modifications.
So, we can say like, it tries to solve the problem of making two (or more) incompatible classes compatible by using an intermediate class that implements a common interface. It allows objects of incompatible interfaces to collaborate.
Understanding the Adapter Pattern
Key components of The Adapter Pattern
- Client: This is the Class, that uses the Target interface, and wants to connect to multiple sources.
- Target: An interface or contract that defines a single API the Client will interact with.
- Adaptee: This is the class or interface that client wants to connect with the Target Interface. It is the existing code or component with an incompatible interface.
- Adapter: The Adapter is a class that implements the Target Interface and wraps an instance of the Adaptee, translating calls between them.
Two Way of Adapter Pattern
While implementing Adapter pattern, there are two approaches – class adapter and object adapter – however both these approaches produce same result.
Class Adapter – This form uses java inheritance and extends the source interface. i.e. the Adapter class extends both the Target interface and the Adaptee class.
Object Adapter – This form uses Java Composition and adapter contains the source object. i.e. the Adapter class implements the Target interface and contains an instance of the Adaptee class.
Adapter Design Pattern Implementation
Let’s take an example of payment support provided by different UPI provider, like PayTM, PhonePe. Each payment gateway has its own unique interface and methods for processing payments, s lets discusses it in details.
Step 1: Adapter interface
Let’s define common interface with a method signature in it, that need to implement by different provider.
public interface UPIGateway { void processPayment(double amount); }
Step 2: Create Payment Gateway Adapters
We will create adapter classes for each specific payment gateway provider like PayTM, PhonePe.
PayTM Adopter:
public class PayTMAdapter implements UPIGateway { private PayTM paymentGateway; public PayTMAdapter(PayTM paymentGateway) { this.paymentGateway = paymentGateway; } @Override public void processPayment(double amount) { // Convert our application's method to PayTM's method paymentGateway.makePayment(amount); } }
PhonePe Adapter
Similarly, we will create an adapter for the PhonePe payment gateway
public class PhonePeAdapter implements UPIGateway { private PhonePe paymentGateway; public PhonePeAdapter(PhonePe paymentGateway) { this.paymentGateway = paymentGateway; } @Override public void processPayment(double amount) { // Convert our application's method to PhonePe's method paymentGateway.charge(amount); } }
These adapter call will take instance of respective gateways and call proceedPayment().
Step 3: Implement Concrete Payment Gateway Providers
In this step we will provide gateways implementation.
PayTM :
public class PayTM { public void makePayment(double amount) { // PayTM payment processing logic System.out.println("Paid $" + amount + " via PayTM."); } }
PhonePe:
public class PhonePe { public void charge(double amount) { // PhonePe payment processing logic System.out.println("Charged $" + amount + " using PhonePe."); } }
Step 4: Client Code
Let’s use all the component that we already define in our client program.
public class PaymentApp { public static void main(String[] args) { UPIGateway payTMGateway = new PayTMAdapter(new PayPM()); UPIGateway phonePeGateway = new PhonePeAdapter(new PhonePe()); double amount = 15000.0; // Process payments using different payment gateways payTMGateway.processPayment(amount); phonePeGateway.processPayment(amount); } }
Adapter Design Pattern Benefits
- Enables collaboration between two or more incompatible interfaces
- It helps you separate the interface or data conversion code from the primary business logic of the program.
- You can introduce new types of adapters, without having to change your code on the Client class.
Adapter Design Pattern Drawbacks
- Can introduce additional complexity if not used judiciously.
- May result in performance overhead due to the translation between interfaces.
- Increases the number of classes and complexity in the codebase.
You may find other post on different topic that might be helpful, Please have a look
- Factory design pattern in Java
- Builder design pattern
- Abstract class and Interface
- Java 8 new feature
- Arrays.asList() and List.of()