Adapter Pattern in C#

Introduction

The Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together. It's useful when you have two incompatible interfaces that need to collaborate, converting the interface of one class into another interface that clients expect. In C#, this pattern is frequently used to make existing classes work with others without modifying their source code.

What is Adapter Pattern in C#?

The Adapter Pattern consists of three main components.

  1. Target: This is the interface that the client code expects.
  2. Adaptee: This is the existing interface that needs adaptation.
  3. Adapter: This class bridges the gap between the Target and the Adaptee by implementing the Target interface and containing an instance of the Adaptee.

Implementation Example in C#

Let's consider an example where we have an existing LegacyPrinter class with a method PrintLegacy(string textToPrint) that we want to adapt to work with a new ModernPrinter the class that expects a Print(string text) method.

// Target interface expected by the client
public interface IModernPrinter
{
    void Print(string text);
}

// Adaptee - existing class with incompatible interface
public class LegacyPrinter
{
    public void PrintLegacy(string textToPrint)
    {
        Console.WriteLine($"Legacy Printer: {textToPrint}");
    }
}

// Adapter implementing the IModernPrinter interface
public class LegacyPrinterAdapter : IModernPrinter
{
    private readonly LegacyPrinter _legacyPrinter;

    public LegacyPrinterAdapter(LegacyPrinter legacyPrinter)
    {
        _legacyPrinter = legacyPrinter;
    }

    public void Print(string text)
    {
        // Adapting the new interface method to the existing one
        _legacyPrinter.PrintLegacy(text);
    }
}

// Client code using the IModernPrinter interface
public class Client
{
    public void PrintUsingModernPrinter(IModernPrinter modernPrinter, string text)
    {
        modernPrinter.Print(text);
    }
}

How the Adapter Pattern Works?

  1. Target Interface (IModernPrinter): Defines the interface expected by the client.
  2. Adaptee (LegacyPrinter): Represents an existing class with an incompatible interface.
  3. Adapter (LegacyPrinterAdapter): Implements the IModernPrinter interface and contains an instance of LegacyPrinter. It adapts the Print method to call the PrintLegacy method.

Usage Example

public class Program
{
    public static void Main(string[] args)
    {
        // Existing legacy printer
        LegacyPrinter legacyPrinter = new LegacyPrinter();

        // Create an adapter to make the legacy printer work with modern interface
        LegacyPrinterAdapter adapter = new LegacyPrinterAdapter(legacyPrinter);

        // Client code interacts with the adapter using the IModernPrinter interface
        Client client = new Client();
        client.PrintUsingModernPrinter(adapter, "Adapting Legacy Printer to Modern Printer");
    }
}

Conclusion

The Adapter Pattern allows us to make existing incompatible classes work together by creating an adapter that converts the interface of one class into another interface that clients expect. This enables better code reusability and interoperability in situations where modifying the existing code is not feasible or desirable.

This pattern is particularly useful when integrating new code with existing systems or libraries that can't be modified directly, allowing them to work seamlessly together.


Similar Articles