Decode Adapter Pattern
Gaurav
Posted on November 26, 2021
When to use
- To wrap an existing class with a new interface.
- To perform impedance matching
Intent
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Components
- Target - defines the domain-specific interface that Client uses.
- Adapter - adapts the interface Adaptee to the Target interface.
- Adaptee - defines an existing interface that needs adapting.
- Client - collaborates with objects conforming to the Target interface.
Structure
Implementation
Assume that you have an e-commerce application which is serving your customers for a long time. This e-commerce application is using a Legacy Order Management System (OMS). Due to the high maintenance cost and degraded performance of the legacy OMS software, you have decided to use a cheap and efficient OMS software which is readily available in the market. However, you realize that the interfaces are different in the new software and it requires a lot of code change in the existing e-commerce application.
Adapter design pattern can be very useful in these situations. Instead of modifying your e-commerce application to use the new interfaces, you can write a 'wrapper' class that acts as a bridge between your e-commerce application and the new OMS software. With this approach, the e-commerce application can still use the old interface.
Adapter design pattern can be implemented in two ways. One using the inheritance method (Class Adapter) and second using the composition (Object Adapter). The following example depicts the implementation of Object adapter.
1 Below is the code that uses the LegacyOMS.
package com.gaurav.adapter;
public class Item {
private String name;
private double price;
public Item(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
package com.gaurav.adapter;
public class Payment {
public String type;
public double amount;
public Payment(String type, double amount) {
super();
this.type = type;
this.amount = amount;
}
public void pay() {
System.out.println(type + " " + amount + "$");
}
}
package com.gaurav.adapter;
import java.util.ArrayList;
import java.util.List;
public class LegacyOMS {
/* The Legacy OMS accepts input in XML format */
List cart = new ArrayList();
List payments = new ArrayList();
public void addItem(Item itemXml) {
cart.add(itemXml);
System.out.println(itemXml.getName() + " " + itemXml.getPrice());
}
public void makePayment(Payment paymentXml) {
payments.add(paymentXml);
paymentXml.pay();
}
}
2 The client code.
package com.gaurav.client;
import com.gaurav.adapter.Item;
import com.gaurav.adapter.OMSAdapter;
import com.gaurav.adapter.Payment;
public class AdapterClient {
public static void main(String[] args) {
/* Create an order and add items */
LegacyOMS oms = new LegacyOMS();
oms.addItem(new Item("Italian Pizza", 6.99));
oms.addItem(new Item("Wine", 9.99));
oms.addItem(new Item("Beer", 5.99));
oms.addItem(new Item("Red Apple", 1.49));
oms.addItem(new Item("Almonds", 11.99));
System.out.println("---------------------------------");
/* Create payment and make payment */
oms.makePayment(new Payment("CASH", 20.00));
oms.makePayment(new Payment("CREDIT", 10.00));
oms.makePayment(new Payment("DEBIT", 10.00));
System.out.println("---------------------------------");
}
}
3 When the OMS needs to be swapped, you can simply create an Adapter class with same interface that the client uses. This adapter/wrapper class "maps" the client interface to the adaptee (New OMS) interface.
package com.gaurav.adapter;
import java.util.ArrayList;
import java.util.List;
public class NewOMS {
/* The new OMS accepts input in JSON format */
List cart = new ArrayList();
List payments = new ArrayList();
public void addToBasket(Item itemJson) {
cart.add(itemJson);
System.out.println(itemJson.getName() + " " + itemJson.getPrice());
}
public void pay(Payment paymentJson) {
payments.add(paymentJson);
paymentJson.pay();
}
}
package com.gaurav.adapter;
public class OMSAdapter {
/* Object Adapter uses composition */
private NewOMS newOMS;
public OMSAdapter() {
newOMS = new NewOMS();
}
public void addItem(Item item) {
convertXmlToJson(item);
newOMS.addToBasket(item);
}
public void makePayment(Payment p) {
convertXmlToJson(p);
newOMS.pay(p);
}
/* The new OMS accepts only Json input.
* Convert the client requests from XML to Json*/
private void convertXmlToJson(Object o) {
System.out.println("Converted from XML to JSON");
}
}
4 The new client code. The client interacts in the same way as before.
package com.gaurav.client;
import com.gaurav.adapter.Item;
import com.gaurav.adapter.OMSAdapter;
import com.gaurav.adapter.Payment;
public class AdapterClient {
public static void main(String[] args) {
/* Create an order and add items */
//LegacyOMS oms = new LegacyOMS();
/* Use Adapter class with the same interface */
OMSAdapter oms = new OMSAdapter();
oms.addItem(new Item("Italian Pizza", 6.99));
oms.addItem(new Item("Wine", 9.99));
oms.addItem(new Item("Beer", 5.99));
oms.addItem(new Item("Red Apple", 1.49));
oms.addItem(new Item("Almonds", 11.99));
System.out.println("---------------------------------");
/* Create payment and make payment */
oms.makePayment(new Payment("CASH", 20.00));
oms.makePayment(new Payment("CREDIT", 10.00));
oms.makePayment(new Payment("DEBIT", 10.00));
System.out.println("---------------------------------");
}
}
Output
[output]
Italian Pizza 6.99
Wine 9.99
Beer 5.99
Red Apple 1.49
Almonds 11.99
---------------------------------
CASH 20.0$
CREDIT 10.0$
DEBIT 10.0$
---------------------------------
Benefits
- Class adapter can override adaptee's behavior.
- Objects adapter allows a single adapter to work with many adaptees.
- Helps achieve reusability and flexibility.
- Client class is not complicated by having to use a different interface and can use polymorphism to swap between different implementations of adapters.
Drawbacks
- Object adapter involves an extra level of indirection.
Real World Examples
- Power adapters
- Memory card adapters
Software Examples
- Wrappers used to adopt 3rd parties libraries and frameworks.
Java SDK Examples
java.util.Arrays asList()
java.util.Collections list()
java.util.Collections enumeration()
java.io.InputStreamReader(InputStream) (returns a Reader)
java.io.OutputStreamWriter(OutputStream) (returns a Writer)
Want to discuss more
Lets have a Coffee
Posted on November 26, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.