Adapter Pattern in C#: Definition, Use Cases, Pros, Cons, and Examples

Adapter Pattern in C#: Definition, Use Cases, Pros, Cons, and Examples

The Adapter Pattern in C# is a structural design pattern that converts the interface of one class into another interface that a client expects, allowing incompatible classes to work together.

The Adapter Pattern acts as a bridge between two incompatible interfaces in a C# application. It wraps an existing class and exposes a new interface that the client code can understand and use. This pattern is commonly used when integrating legacy systems, third-party libraries, or external APIs into modern applications without modifying existing source code. The adapter translates requests from the client into calls that the wrapped object can process. By using adapters, developers can improve code reusability, maintainability, and flexibility while reducing changes to existing systems.

Why We Use Adapter Pattern in C#?

We use the Adapter Pattern in C# to solve compatibility problems between classes that were not originally designed to work together. It helps developers integrate old systems with new architectures without changing existing business logic. The pattern also allows applications to use third-party libraries through a unified interface. In enterprise software, adapters reduce tight coupling and make systems easier to maintain and extend.

Main reasons for using it:

• Integrate incompatible interfaces
• Reuse existing classes without modification
• Connect legacy code with modern applications
• Simplify third-party library integration
• Improve maintainability and flexibility
• Reduce dependency between components

When Should We Use Adapter Pattern in C#?

The Adapter Pattern should be used when:

• Two existing classes have incompatible interfaces
• You want to reuse a legacy class in a new system
• You need to integrate third-party APIs or SDKs
• Refactoring existing code is risky or expensive
• Multiple systems communicate using different data formats
• You want to standardize communication between components

Typical programming problems solved by the Adapter Pattern:

• Legacy system integration
• Payment gateway integration
• File format conversion
• API wrapper implementation
• Database provider abstraction
• External service communication
• Hardware/device driver compatibility

Structure of Adapter Pattern in C#

The Adapter Pattern usually contains these components:

Component Responsibility
Target Expected interface used by the client
Adapter Converts requests between interfaces
Adaptee Existing incompatible class
Client Uses the target interface

Adapter Pattern Examples in C#

Example 1: Legacy Logger Integration

A modern application expects an ILogger interface, but an old logging system uses a different method.

using System;

public interface ILogger
{
    void Log(string message);
}

public class LegacyLogger
{
    public void WriteLog(string text)
    {
        Console.WriteLine("Legacy Log: " + text);
    }
}

public class LoggerAdapter : ILogger
{
    private readonly LegacyLogger _legacyLogger;

    public LoggerAdapter(LegacyLogger legacyLogger)
    {
        _legacyLogger = legacyLogger;
    }

    public void Log(string message)
    {
        _legacyLogger.WriteLog(message);
    }
}

class Program
{
    static void Main()
    {
        ILogger logger = new LoggerAdapter(new LegacyLogger());
        logger.Log("Application started.");
    }
}

Example 2: Payment Gateway Adapter

An e-commerce system expects a standard payment interface, but different payment providers expose different APIs.

using System;

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

public class PayPalService
{
    public void SendPayment(decimal total)
    {
        Console.WriteLine($"Paid {total} using PayPal.");
    }
}

public class PayPalAdapter : IPaymentProcessor
{
    private readonly PayPalService _payPalService;

    public PayPalAdapter(PayPalService payPalService)
    {
        _payPalService = payPalService;
    }

    public void Pay(decimal amount)
    {
        _payPalService.SendPayment(amount);
    }
}

class Program
{
    static void Main()
    {
        IPaymentProcessor processor =
            new PayPalAdapter(new PayPalService());

        processor.Pay(250.50m);
    }
}

Example 3: XML to JSON Data Adapter

A system expects JSON data, but an external service returns XML.

using System;

public interface IJsonData
{
    string GetJson();
}

public class XmlService
{
    public string GetXmlData()
    {
        return "John";
    }
}

public class XmlToJsonAdapter : IJsonData
{
    private readonly XmlService _xmlService;

    public XmlToJsonAdapter(XmlService xmlService)
    {
        _xmlService = xmlService;
    }

    public string GetJson()
    {
        string xml = _xmlService.GetXmlData();

        // Simulated conversion
        return "{ \"name\": \"John\" }";
    }
}

class Program
{
    static void Main()
    {
        IJsonData data =
            new XmlToJsonAdapter(new XmlService());

        Console.WriteLine(data.GetJson());
    }
}

Most Common Real-World Use Cases of Adapter Pattern in C#

Use Case Description
Legacy System Integration Connecting old enterprise systems to modern applications
Payment Gateway Integration Standardizing different payment provider APIs
Third-Party Library Wrappers Adapting external SDKs to internal interfaces
File Format Conversion XML, JSON, CSV, or binary transformations
Database Provider Adapters Supporting multiple database engines
Hardware/Device Drivers Making devices compatible with software systems
Cloud Service Integration Standardizing AWS, Azure, or Google APIs

Advantages of Adapter Pattern in C#

1. Improves Reusability

Existing classes can be reused without modifying their source code.

2. Reduces Code Changes

Legacy or third-party code remains untouched.

3. Increases Flexibility

Applications can support multiple incompatible systems.

4. Encourages SOLID Principles

Especially supports:

• Open/Closed Principle
• Single Responsibility Principle

5. Simplifies Integration

External APIs and services become easier to integrate.

6. Better Maintainability

Changes are isolated inside adapter classes.

Disadvantages (Weak Points) of Adapter Pattern in C#

1. Increased Complexity

Additional classes increase system complexity.

2. More Indirection

Extra abstraction layers can make debugging harder.

3. Performance Overhead

Adapters introduce small runtime overhead due to delegation.

4. Too Many Adapters

Large systems may become difficult to manage if adapters are overused.

5. Harder Understanding for Beginners

Developers unfamiliar with design patterns may find the architecture confusing.

Adapter Pattern vs Similar Patterns

Pattern Main Purpose Key Difference from Adapter Typical Usage
Adapter Convert incompatible interfaces Focuses on compatibility Legacy integration, API wrappers
Facade Simplify a complex subsystem Provides simplified interface, not conversion Complex framework abstraction
Decorator Add new behavior dynamically Adds functionality instead of adapting interfaces Logging, caching, validation
Bridge Separate abstraction from implementation Designed upfront for extensibility Cross-platform systems
Proxy Control access to an object Focuses on access control and optimization Lazy loading, security, remote access

Summary

The Adapter Pattern in C# is one of the most practical structural design patterns for integrating incompatible systems. It enables legacy code reuse, simplifies third-party integrations, and improves software flexibility without changing existing implementations. Although it adds some complexity and abstraction layers, it is extremely valuable in enterprise applications, API integrations, and modernization projects.

Structural Patterns in C#

6. Adapter
7. Bridge
8. Composite
9. Decorator
10. Facade
11. Flyweight
12. Proxy