C# Adapter Pattern

Summary: in this tutorial, you’ll learn how to use the C# Adapter pattern to enable classes of incompatible interfaces to work together.

Introduction to the C# Adapter pattern

The Adapter pattern converts an interface into another interface that clients expect. The Adapter pattern allows classes of incompatible interfaces to work together.

Suppose you’re traveling to a foreign country and you want to charge your phone. The problem is that the electric socket in that country has a different shape than your phone charger’s plug.

To solve this problem, you can use an adapter that converts the foreign socket into your phone charger’s plug shape.

This adapter serves as a wrapper that translates the interface of the foreign socket into the interface that your phone charger expects. By doing this, you can charge your phone without changing the foreign socket.

C# Adapter Pattern

Similarly, the Adapter pattern creates an adapter that converts the interface of an object into an interface that clients expect, allowing them to work together without modifying their code.

The Adapter pattern has two variants: object adapter and class adapter.

Object Adapter

The following UML diagram illustrates the object adapter:

C# Object Adapter Pattern

In this Object Adapter UML diagram:

  • ITarget interface: This is the interface that the client expects to work with. It’s also the interface that the Adapter class will implement.
  • Adaptee: is the class that has an interface that is not compatible with the ITarget interface. It is the class that the Adapter class will wrap and adapt.
  • Adapter: is the class that implements the target interface and wraps the Adaptee object. The Adapter class is responsible for translating the calls from the ITarget interface to the Adaptee‘s interface and delegating the work to it.

In this pattern, the Adapter has a member which is the Adaptee object. The Operation() method of the Adapter class calls the SpecificOperation() method of the Adaptee object.

Object Adapter example

Suppose you need to implement a payment feature in your system application. And you have to use a third-party class called PaymentProcessor.

The PaymentProcessor class has a method called ProcessPayment, which accepts a decimal argument that represents the payment amount:

public class PaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        Console.WriteLine($"You have paid {amount:C}.");
    }
}Code language: C# (cs)

But your application uses an interface called IPaymentProvider with a method MakePayment. The problem is that the MakePayment method accepts two arguments: a string that represents the payment details and a decimal value that represents the payment amount:

public interface IPaymentProvider
{
    void MakePayment(string details, decimal amount);
}Code language: C# (cs)

As you can see, the interface that your application expects is IPaymentProvider which is incompatible with the class PaymentProcessor.

To solve this problem, you can apply the object Adapter pattern.

To use the PaymentProcessor with your IPaymentProvider interface, you can create an Adapter class called PaymentProviderAdapter.

The PaymentProviderAdapter needs to implement the IPaymentProvider interface and wrap the PaymentProcessor class. Also, the MakePayment method of the PaymnetProviderAdapter needs to translate the call to the ProcessPayment method of the PaymentProcessor class:

// The adapter class that adapts the PaymentProcessor
// to the IPaymentProvider interface
public class PaymentProviderAdapter : IPaymentProvider
{
    private readonly PaymentProcessor _paymentProcessor;

    public PaymentProviderAdapter(PaymentProcessor paymentProcessor)
    {
        _paymentProcessor = paymentProcessor;
    }

    public void MakePayment(string details, decimal amount)
    {
        Console.WriteLine($"Making a payment for: {details}");
        _paymentProcessor.ProcessPayment(amount);
    }
}Code language: C# (cs)

Now, you can use the PaymentProcessor class in your application as follows:

public class Program
{
    public static void Main(string[] args)
    {
        var paymentProcessor = new PaymentProcessor();
        var paymentProvider = new PaymentProviderAdapter(paymentProcessor);

        paymentProvider.MakePayment("C# design pattern course", 100.0m);
    }
}Code language: C# (cs)

In the Program class:

  • First, create an instance of the PaymentProcess class and create an instance of the PaymentProviderAdapter class that wraps the PaymentProcess‘s object.
  • Second, call the MakePayment of the instance of the PaymentProviderAdapter class to make a payment, which will in turn call the ProcessPayment method on the PaymentProcessor class.

Put it all together.

namespace ObjectAdapter;

// The third-party PaymentProcessor class
public class PaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        Console.WriteLine($"You have paid {amount:C}.");
    }
}

// your application's IPaymentProvider interface
public interface IPaymentProvider
{
    void MakePayment(string details, decimal amount);
}

// The adapter class that adapts the PaymentProcessor
// to the IPaymentProvider interface
public class PaymentProviderAdapter : IPaymentProvider
{
    private readonly PaymentProcessor _paymentProcessor;

    public PaymentProviderAdapter(PaymentProcessor paymentProcessor)
    {
        _paymentProcessor = paymentProcessor;
    }

    public void MakePayment(string details, decimal amount)
    {
        Console.WriteLine($"Making a payment for: {details}");
        _paymentProcessor.ProcessPayment(amount);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var paymentProcessor = new PaymentProcessor();
        var paymentProvider = new PaymentProviderAdapter(paymentProcessor);

        paymentProvider.MakePayment("C# design pattern course", 100.0m);
    }
}Code language: C# (cs)

Output:

Making a payment for: C# design pattern course
You have paid $100.00.Code language: C# (cs)

Class Adapter pattern

The Object Adapter pattern uses composition to wrap the incompatible class. Meanwhile, the class Adapter pattern uses inheritance.

In the Class Adapter pattern, the Adapter class extends both the Adaptee and target classes. The Adapter class then overrides the method of the target class and calls the corresponding method of the Adaptee interface.

C# doesn’t support multiple inheritances that allow a class to extend two or more classes. But, a class can extend a class and implement multiple interfaces.

The following UML diagram illustrates the Class Adapter pattern:

In this diagram, the Adapter class inherits from the Adaptee class instead of composing it. And the Operation() method of the Adapter class calls the SpecificOperation() method of the Adaptee class.

Class Adapter example

The following is the same as the example that uses the object adapter pattern, but uses the Class Adapter pattern instead:

namespace ClassAdapter;

// The third-party PaymentProcessor class
public class PaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        Console.WriteLine($"You have paid {amount:C}.");
    }
}

// Our application's IPaymentProvider interface
public interface IPaymentProvider
{
    void MakePayment(string details, decimal amount);
}

// The adapter class that adapts the PaymentProcessor to the IPaymentProvider interface
public class PaymentProviderAdapter : PaymentProcessor, IPaymentProvider
{
    public void MakePayment(string details, decimal amount)
    {
        Console.WriteLine($"Making a payment for: {details}");
        ProcessPayment(amount);
    }
}

class Program
{
    public static void Main(string[] args)
    {
        var paymentProvider = new PaymentProviderAdapter();

        paymentProvider.MakePayment("C# design pattern course", 100.0m);
    }
}Code language: C# (cs)

Summary

  • The Adapter pattern allows the objects with incompatible interfaces to work together.
  • The Object Adapter pattern uses the composition while the Class Adapter class uses inheritance.
Was this tutorial helpful ?