Understanding the Adapter Design Pattern in Java
The Adapter Design Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. It acts as a bridge, converting the interface of a class into another interface clients expect, enabling collaboration despite incompatibility.
In this post, we will explore the Adapter Pattern with practical real-world examples, including how it helps adapt different data formats or object interfaces.
What is the Adapter Design Pattern?
Adapter Pattern allows wrapping an incompatible object with an adapter so that it matches the expected interface. This is commonly used when integrating third-party libraries, legacy code, or systems with varying data formats.
Real-World Example: Swiggy Food & Grocery Delivery
Imagine the Swiggy app initially supports only food items from restaurants. With the pandemic, it needs to support grocery items from stores, which have a different interface. Using the Adapter Pattern, grocery items are adapted to the existing food item interface so they can be seamlessly integrated and ordered through the same app.
Key Components
- Target Interface: The interface expected by the client, for example,
Itemwith methods likegetItemName(),getPrice(), andgetRestaurantName(). - Adaptee Interface: An incompatible interface that needs adapting, such as
GroceryItemwith methodsgetItemName(),getPrice(), andgetStoreName(). - Adapter Class: Implements the Target interface and holds an instance of the Adaptee, translating requests from Target to Adaptee.
- Client: Works with objects through the Target interface without knowing about the Adaptee or Adapter.
Java Code Example
// Target Interface
public interface Item {
String getItemName();
double getPrice();
String getRestaurantName();
}
// Adaptee Interface
public interface GroceryItem {
String getItemName();
double getPrice();
String getStoreName();
}
// Concrete Target Implementation (Food Item)
public class FoodItem implements Item {
private String itemName;
private double price;
private String restaurantName;
public FoodItem(String itemName, double price, String restaurantName) {
this.itemName = itemName;
this.price = price;
this.restaurantName = restaurantName;
}
public String getItemName() { return itemName; }
public double getPrice() { return price; }
public String getRestaurantName() { return restaurantName; }
}
// Concrete Adaptee Implementation (Grocery Product)
public class GroceryProduct implements GroceryItem {
private String itemName;
private double price;
private String storeName;
public GroceryProduct(String itemName, double price, String storeName) {
this.itemName = itemName;
this.price = price;
this.storeName = storeName;
}
public String getItemName() { return itemName; }
public double getPrice() { return price; }
public String getStoreName() { return storeName; }
}
// Adapter Class
public class GroceryItemAdapter implements Item {
private GroceryItem groceryItem;
public GroceryItemAdapter(GroceryItem groceryItem) {
this.groceryItem = groceryItem;
}
public String getItemName() {
return groceryItem.getItemName();
}
public double getPrice() {
return groceryItem.getPrice();
}
public String getRestaurantName() {
// Adapter translates storeName to restaurantName
return groceryItem.getStoreName();
}
}
Using the Adapter in Client Code
import java.util.ArrayList;
import java.util.List;
public class SwiggyStore {
private List- itemList = new ArrayList<>();
public void addItem(Item item) {
itemList.add(item);
}
public void showItems() {
for (Item item : itemList) {
System.out.println("Item: " + item.getItemName() +
", Price: " + item.getPrice() +
", Restaurant/Store: " + item.getRestaurantName());
}
}
public static void main(String[] args) {
SwiggyStore store = new SwiggyStore();
// Adding food items directly
store.addItem(new FoodItem("Burger", 150, "Food Restaurant"));
store.addItem(new FoodItem("Pizza", 250, "Food Restaurant"));
// Adding grocery items via adapter
GroceryItem rice = new GroceryProduct("Rice", 500, "Grocery Store");
Item riceAdapter = new GroceryItemAdapter(rice);
store.addItem(riceAdapter);
store.showItems();
}
}
How It Works
The client (SwiggyStore) treats food and grocery items uniformly through the Item interface. The GroceryItemAdapter converts calls from Item to GroceryItem, allowing incompatible interfaces to work together smoothly.
Advantages
- Enables reusing existing incompatible classes without modifying them.
- Promotes flexibility and integration with third-party or legacy systems.
- Decouples client code from specific implementations.
Interview Tips
- Clearly describe the difference between Adapter and Decorator patterns.
- Explain real-world use cases like electrical outlets or software integration.
- Demonstrate understanding through code showing interface adaptation.
Summary
The Adapter Design Pattern is essential for integrating incompatible interfaces and making systems work together. It’s one of the easiest and most practical structural design patterns to implement and explain, making it a favorite in technical interviews.
If this post clarified the Adapter Pattern, feel free to comment your questions and subscribe for more Java design pattern tutorials.
Comments
Post a Comment