Adapter
================
An adapter is an object that changes the interface of one class so that it can be used with classes from a different abstraction level or domain.
Overview
In software development, adapters allow objects to work together in ways that are not possible by virtue of their own interfaces. They provide a way for classes from different domains or layers to interact with each other. This is useful when there are significant differences between the interfaces or implementations of two classes, making it difficult for them to coexist.
Components
A typical adapter consists of three main components:
- Target Interface: The interface that the adapter will change.
- Adapter Class: The class that implements the target interface and adapts it for another interface or domain.
- Wrapper Object: An object that wraps around an instance of a class implementing the original target interface, providing access to its methods through the adapted interface.
Working Example
Sample Scenario
Suppose we have two domains: Customer ( Domain-1) and Order (Domain-2). The Customer domain represents individual customers, while the Order domain represents orders with various payment methods. However, some payment methods do not support credit cards.
Without adapters, we would need to create a separate class for each Payment Method that supports credit cards, which is impractical and cluttered. With adapters, we can use an adapter class that implements the target interface (Customer) for all domains.
Code Example (C#)
// Target Interface (<a href="/Domain-1" class="missing-article">Domain-1</a>)
public interface <a href="/ICreditCardPaymentMethod" class="missing-article">ICreditCardPaymentMethod</a>
{
decimal CalculateTotal();
}
// Target Interface (<a href="/Domain-2" class="missing-article">Domain-2</a>)
public interface <a href="/OrderPaymentMethod" class="missing-article">OrderPaymentMethod</a>
{
}
// Concrete Implementation of Target Interfaces (<a href="/Domain-1" class="missing-article">Domain-1</a> and <a href="/Domain-2" class="missing-article">Domain-2</a>)
public class CreditCardPayment : <a href="/ICreditCardPaymentMethod" class="missing-article">ICreditCardPaymentMethod</a>, <a href="/OrderPaymentMethod" class="missing-article">OrderPaymentMethod</a>
{
public decimal CalculateTotal() => 100m;
}
public class PayPalPayment : <a href="/ICreditCardPaymentMethod" class="missing-article">ICreditCardPaymentMethod</a>, <a href="/OrderPaymentMethod" class="missing-article">OrderPaymentMethod</a>
{
public decimal CalculateTotal() => 200m;
}
// Adapter Class (<a href="/Domain-1" class="missing-article">Domain-1</a> and <a href="/Domain-2" class="missing-article">Domain-2</a>)
public interface <a href="/IPaymentMethod" class="missing-article">IPaymentMethod</a>
{
void ProcessOrder(Order order);
}
public class <a href="/PaymentAdapter" class="missing-article">PaymentAdapter</a> : <a href="/IPaymentMethod" class="missing-article">IPaymentMethod</a>
{
private readonly <a href="/ICreditCardPaymentMethod" class="missing-article">ICreditCardPaymentMethod</a> creditCardPayment;
public <a href="/PaymentAdapter" class="missing-article">PaymentAdapter</a>(<a href="/ICreditCardPaymentMethod" class="missing-article">ICreditCardPaymentMethod</a> creditCardPayment)
{
this.creditCardPayment = creditCardPayment;
}
public void ProcessOrder(Order order)
{
var paymentMethod = new CreditCardPayment();
var total = creditCardPayment.CalculateTotal();
order.SetPaymentMethod(paymentMethod);
paymentMethod.ProcessOrder(order);
}
}
// Usage Example
public class <a href="/CustomerOrderProcessor" class="missing-article">CustomerOrderProcessor</a>
{
private readonly <a href="/IPaymentMethod" class="missing-article">IPaymentMethod</a> paymentMethod;
public <a href="/CustomerOrderProcessor" class="missing-article">CustomerOrderProcessor</a>(<a href="/IPaymentMethod" class="missing-article">IPaymentMethod</a> paymentMethod)
{
this.paymentMethod = paymentMethod;
}
public void ProcessCustomerOrder(Order order)
{
order.SetPaymentMethod(paymentMethod);
paymentMethod.ProcessOrder(order);
}
}
Benefits
The Adapter Pattern provides several benefits:
- Decoupling: It decouples the dependent classes, making it easier to change or replace one without affecting the other.
- Extensibility: Adapters can be easily added or removed without modifying the original code.
- Flexibility: The Adapter Pattern allows for more flexibility in terms of adapting different interfaces and domains.
Considerations
While the Adapter Pattern is useful, there are some considerations to keep in mind:
- Added Complexity: Adding adapters introduces complexity into the system, which may lead to maintenance issues if not implemented correctly.
- Limited Flexibility: If an adapter is too specific or rigid, it might not be able to accommodate changes in the underlying interfaces or domains.
In conclusion, the Adapter Pattern is a powerful tool for working with different interfaces and domains in software development. By understanding its components and benefits, you can effectively use adapters to improve the flexibility and maintainability of your codebase.