Understanding Adapter Design Pattern

The Adapter Pattern converts the interface of a class into another interface that a client expects.This post will help you understanding deeply the Adapter pattern in what is it, why need it ect.

Adapter Design Pattern in C# – A Practical Guide/h2>

Getting Started

In modern software development, systems rarely exist in isolation. Applications often need to communicate with third-party libraries, legacy systems, external APIs, databases, or microservices. Each of these components may expose different interfaces, naming conventions, data formats, or method signatures. When these interfaces don’t match the expectations of your application, integration becomes difficult.

This is where the Adapter Pattern becomes extremely valuable.

The Adapter Pattern is a structural design pattern that allows two incompatible interfaces to work together without modifying their existing source code. It acts as a wrapper or translator that converts one interface into another that the client expects.

Instead of rewriting legacy code or changing your existing application logic, you introduce an adapter class that sits between them and handles the translation.

Why Do We Need the Adapter Pattern?

Imagine you are integrating a third-party library into your system. Your application expects a certain interface, but the third-party class provides a different one.

Instead of rewriting existing code (which may not be possible or safe), you create an adapter that translates between the two interfaces.

This helps:
  • Reuse existing classes without modifying them
  • Integrate legacy or third-party systems
  • Follow the Open/Closed Principle (open for extension, closed for modification)

Types of Adapter Pattern in C#

There are two common implementations:
  1. Class Adapter (using inheritance)
  2. Object Adapter (using composition – most common in C#)

Because C# does not support multiple inheritance of classes, the Object Adapter approach is typically preferred.

Structure of Adapter Pattern

The pattern usually contains:
  • Target – The interface expected by the client
  • Adaptee – The existing class with incompatible interface
  • Adapter – The class that converts the interface
  • Client – The class that uses the Target interface

Example: Payment System Integration

Suppose your application is designed around a clean abstraction such as an IPaymentProcessor interface with a method like ProcessPayment(decimal amount).

public interface IPaymentProcessor
{
  void ProcessPayment(decimal amount);
}

All your business logic, checkout workflows, and billing services depend on this interface. However, the third-party payment provider you need to use exposes a completely different API—for example, a class named ThirdPartyPaymentGateway with a method called MakeTransaction(double value).

public class ThirdPartyPaymentGateway
{
  public void MakeTransaction(double value)
  {
    Console.WriteLine($"Processing payment of {value}");
  }
}

The method name differs, the parameter type is not the same, and the interface structure does not align with your application’s expectations. Since you typically cannot modify third-party source code, directly using this class throughout your system would tightly couple your business logic to an external dependency and break your architectural consistency.

To solve this problem, you introduce a PaymentAdapter class that implements your IPaymentProcessor interface while internally wrapping an instance of ThirdPartyPaymentGateway. When ProcessPaymentis called, the adapter translates the call by converting the decimal amount to double and delegating the request to MakeTransaction.

From the perspective of the rest of your application, nothing changes—it continues to interact only with IPaymentProcessor. The adapter isolates all incompatibility and conversion logic in one place, keeping your system clean, maintainable, and loosely coupled. If you later switch to a different payment provider, you simply create a new adapter without modifying your core business logic, demonstrating the flexibility and power of the Adapter Pattern in enterprise-level C# applications.

Implementing the Adapter Using Object Adapter

public class PaymentAdapter : IPaymentProcessor
{
  private readonly ThirdPartyPaymentGateway _gateway;
  public PaymentAdapter(ThirdPartyPaymentGateway gateway)
  {
    _gateway = gateway;
  }
  public void ProcessPayment(decimal amount)
  {
    // Convert decimal to double
    _gateway.MakeTransaction((double)amount);
  }
}

Client Code
class Program
{
  static void Main(string[] args)
  {
    ThirdPartyPaymentGateway gateway = new ThirdPartyPaymentGateway();
    IPaymentProcessor paymentProcessor = new PaymentAdapter(gateway);
    paymentProcessor.ProcessPayment(150.75m);
  }
}

How It Works
  1. The client interacts with IPaymentProcessor.
  2. PaymentAdapter implements IPaymentProcessor.
  3. Inside the adapter, calls are translated to ThirdPartyPaymentGateway.
  4. The client remains unaware of the third-party implementation.

Summary

The Adapter Pattern is a powerful structural pattern that allows incompatible systems to work together without modifying their source code. I hope this was helpfult to you.

Thanks

Kailash Chandra Behera

I am an IT professional with over 13 years of experience in the full software development life cycle for Windows, services, and web-based applications using Microsoft .NET technologies.

Previous Post Next Post

نموذج الاتصال