Bridging Interfaces with the Adapter Pattern

Make incompatible systems work together seamlessly

Posted by Hüseyin Sekmenoğlu on February 24, 2021 Design Patterns

πŸ€” What Is the Adapter Pattern?

The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. It acts as a bridge between two different systems by wrapping one interface inside another.

Think of it as a plug adapter that lets a foreign device connect to a local socket.


πŸ“¦ When to Use It

Use the Adapter Pattern when:

  • You want to reuse existing classes without modifying their source code

  • You need to integrate with third-party APIs that don’t match your expected interface

  • You are migrating from legacy systems and need to maintain backward compatibility

Typical scenarios:

  • Wrapping old code with a modern interface

  • Adapting third-party libraries to your domain model

  • Integrating services with mismatched contracts


πŸ› οΈ How It Works

The Adapter Pattern includes:

  1. A client expecting a specific interface

  2. A legacy or incompatible class with a different interface

  3. An adapter that translates the incompatible interface into the one expected by the client

C# Example: Audio Player Adapting Media Formats

// Target interface
public interface IAudioPlayer
{
    void Play(string fileName);
}

// Adaptee with a different interface
public class Mp4Player
{
    public void PlayMp4(string fileName)
    {
        Console.WriteLine($"Playing MP4 file: {fileName}");
    }
}

// Adapter
public class Mp4Adapter : IAudioPlayer
{
    private readonly Mp4Player _mp4Player;

    public Mp4Adapter(Mp4Player mp4Player)
    {
        _mp4Player = mp4Player;
    }

    public void Play(string fileName)
    {
        _mp4Player.PlayMp4(fileName);
    }
}

Usage

IAudioPlayer player = new Mp4Adapter(new Mp4Player());
player.Play("song.mp4");

βœ… Advantages

  • Promotes reusability by allowing incompatible interfaces to cooperate

  • Follows the Open/Closed Principle since you don’t modify existing code

  • Makes your system more flexible and extensible


❌ Disadvantages

  • Introduces extra layers which may affect performance

  • Can become complicated if too many adapters are used

  • May hide the limitations or quirks of the adapted code


πŸ§ͺ Testing Benefits

  • You can mock the adapter interface for unit tests

  • Allows isolation of external libraries or legacy systems in your tests

  • Helps enforce separation of concerns by wrapping third-party logic


🌍 Real-World Use Cases

  • Payment gateways: Wrapping various APIs into a common payment interface

  • Logging frameworks: Adapting one logging provider to match a custom interface

  • UI components: Bridging incompatible widgets in cross-platform toolkits

  • File systems: Abstracting platform-specific operations under a unified interface


πŸ”— Related Patterns

  • Facade: Simplifies a subsystem but keeps interfaces compatible

  • Decorator: Adds new functionality while preserving the interface

  • Bridge: Decouples abstraction from implementation, while Adapter changes the interface


🎯 Final Thoughts

The Adapter Pattern lets you connect the dots between systems that were never designed to talk to each other. It is especially useful when working with legacy code, third-party APIs or integrating heterogeneous systems. Just be careful not to overuse adapters or wrap poorly designed code without addressing underlying issues.