DEV Community

Cover image for Understanding the Adapter Pattern in C#
Daniel Azevedo
Daniel Azevedo

Posted on

Understanding the Adapter Pattern in C#

Hi devs :)

When working with different systems or libraries, we often come across the issue of incompatible interfaces. This is where the Adapter Pattern becomes incredibly useful. It's one of those design patterns that you don’t think about until you suddenly need it, but when you do, it feels like a lifesaver.

In this post, I’ll break down what the Adapter Pattern is and walk you through a simple example using C#. Hopefully, this will make it easier for anyone working with legacy systems or integrating third-party libraries.


What is the Adapter Pattern?

At its core, the Adapter Pattern allows objects with incompatible interfaces to work together. You can think of it as a "translator" between two classes. It’s like when you plug your phone charger into an international adapter – even though the socket is different, the adapter helps make the connection.


When Should You Use It?

In a real-world scenario, you’d use the Adapter Pattern when you have a class that can’t interact directly with another because of differences in their interfaces. Instead of changing the existing code (which is usually not possible or a bad idea), we create an adapter to bridge the gap between them.


Adapter Pattern in Action: Payroll System Example

Let’s take a practical example involving a payroll system. Imagine you’ve got an old system that calculates salaries using one method, and now you’ve added a new library that uses a different approach. Instead of rewriting the entire old system, you can simply create an adapter.

Step 1: Define the Target Interface

This is the interface that the client expects to work with. In our case, it’s a simplified payroll processor.



public interface IPayrollProcessor
{
    void ProcessSalary(string employeeName, decimal salary);
}


Enter fullscreen mode Exit fullscreen mode

Step 2: Existing Class with Incompatible Interface

Now, let’s say we have an existing class OldPayrollSystem that calculates salaries differently, but it doesn’t implement the IPayrollProcessor interface.



public class OldPayrollSystem
{
    public void RunPayroll(string name, decimal baseSalary)
    {
        Console.WriteLine($"Processing payroll for {name} with base salary {baseSalary}");
    }
}


Enter fullscreen mode Exit fullscreen mode

Step 3: Create the Adapter

We’ll now create an adapter that implements IPayrollProcessor and makes OldPayrollSystem compatible with our new payroll system.



public class PayrollAdapter : IPayrollProcessor
{
    private readonly OldPayrollSystem _oldSystem;

    public PayrollAdapter(OldPayrollSystem oldSystem)
    {
        _oldSystem = oldSystem;
    }

    public void ProcessSalary(string employeeName, decimal salary)
    {
        _oldSystem.RunPayroll(employeeName, salary);  // Adapts the old method to match the new interface
    }
}


Enter fullscreen mode Exit fullscreen mode

Step 4: Client Code

Now the client can use the adapter without worrying about the underlying implementation.



class Program
{
    static void Main(string[] args)
    {
        // Old system
        OldPayrollSystem oldSystem = new OldPayrollSystem();

        // Adapter
        IPayrollProcessor payrollProcessor = new PayrollAdapter(oldSystem);

        // Process salary using the adapter
        payrollProcessor.ProcessSalary("John Doe", 5000);

        // Output: Processing payroll for John Doe with base salary 5000
    }
}


Enter fullscreen mode Exit fullscreen mode

In this example, we’ve taken the old payroll system and used an adapter to make it compatible with the new system. The client doesn’t even know the difference!


Key Takeaways

  • Decoupling: The Adapter Pattern allows systems to interact without being tightly coupled. This makes the code easier to maintain and extend.
  • Backward Compatibility: It’s perfect for integrating legacy systems with new code, avoiding the need for rewriting large parts of the application.
  • Simplicity: The adapter acts as a simple wrapper around the incompatible class, transforming the interface in a way that the client expects.

When NOT to Use It

Like with most patterns, the Adapter Pattern isn’t always the best solution. If you can modify the source code of the incompatible class directly, it might be better to update it instead of introducing an adapter. Also, if you find yourself using multiple adapters, it might be a sign that your system needs a more significant redesign.


Conclusion

The Adapter Pattern is incredibly useful when integrating different systems, especially when dealing with legacy code or third-party libraries. It allows us to keep our systems flexible and adaptable without compromising on code quality.

I hope this explanation and example make the Adapter Pattern a little clearer!

Keep coding :)

Top comments (0)