Adapter Design Pattern


Here, we will explain Adapter Design Pattern with example in C#

Adapter Design Pattern

The Adapter design pattern is a structural pattern that allows objects with incompatible interfaces to work together. It acts as a bridge between two different interfaces, converting one interface into another expected by the client.

Incompatible Interfaces

Incompatible interfaces refer to situations where two software components or classes cannot work together directly because they have different methods, signatures, or formats for interacting with each other. This means the way one class provides its functionality does not match the way another class or client expects to receive or use it.

Example

  • Different method names - One class might have a method FetchData() to get data, but the client expects a method called GetInfo().

Advantages of the Adapter Pattern

  1. Reusability - Allows the reuse of existing classes that may not have compatible interfaces, without modifying the code of those classes.
  2. Flexibility - You can implement multiple adapters to work with different interfaces, which enhances the flexibility of the system.
  3. Separation of Concerns - The adapter isolates the client code from the specific details of interfacing with external systems or legacy code.
  4. Simplifies Client Code - The client interacts with a consistent interface, regardless of the actual implementation, making the code simpler and easier to maintain.

When to Use the Adapter Pattern

  1. Legacy Code Integration - When integrating older or third-party systems that have incompatible interfaces with the new system.
  2. Class Compatibility - When you have existing classes whose interfaces are different from the ones expected by the client.
  3. Third-party Libraries - When you need to adapt external libraries or frameworks to conform to your application’s interface requirements.
  4. Different Data Formats - When handling varying data formats that need to be converted to work with your existing system.

Problems in a System Without Adapter

  1. Incompatibility - Without an adapter, incompatible interfaces or classes cannot work together, leading to redundancy or complex code changes.
  2. Code Duplication - Developers might need to write additional code or modify the existing classes, leading to duplication or even potential code breakage.
  3. Tight Coupling - Without an adapter, the client code becomes tightly coupled to specific implementations, making it harder to maintain or scale the system.
  4. Lack of Flexibility - Without an adapter, it’s difficult to swap out or change external systems without impacting the entire client code.

Example - Media Player Application with Adapter

In a media player application, you have a client expecting an interface that can play MP3 files, but you need to integrate third-party libraries that can only play MP4 or VLC formats. The Adapter pattern allows you to wrap these incompatible media formats in a consistent interface so the client can play all of them through a unified interface.

Adapter Design Pattern

Here is an illustration of a media player using the Adapter design pattern. It shows how different media formats (MP4, VLC) can be connected and adapted to a unified media player interface using adapters. This visually represents how incompatible formats can work together in a seamless system.

Below is an example using C# to demonstrate the Adapter Design Pattern in the context of a media player that plays MP3 files but integrates third-party libraries for MP4 and VLC formats. The adapter will allow these formats to work with a consistent interface.

Step-by-step explanation

  1. Target Interface (IMediaPlayer) - Defines the method expected by the client (Play method for MP3 files).
  2. Adaptee Classes (Mp4Player, VlcPlayer) - Represent third-party or incompatible classes with different interfaces (PlayMp4, PlayVlc).
  3. Adapter Classes (Mp4Adapter, VlcAdapter) - Adapt the interfaces of the third-party players to match the expected IMediaPlayer interface.
  4. Client Code - Uses the IMediaPlayer interface to interact with all media types seamlessly.

C# Code Example

using System;

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

// Adaptee Class 1: Third-party MP4 player
public class Mp4Player
{
    public void PlayMp4(string fileName)
    {
        Console.WriteLine("Playing MP4 file: " + fileName);
    }
}

// Adaptee Class 2: Third-party VLC player
public class VlcPlayer
{
    public void PlayVlc(string fileName)
    {
        Console.WriteLine("Playing VLC file: " + fileName);
    }
}

// Adapter Class for MP4 Player
public class Mp4Adapter : IMediaPlayer
{
    private Mp4Player _mp4Player;

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

    public void Play(string fileName)
    {
        _mp4Player.PlayMp4(fileName);  // Adapted to use PlayMp4 method
    }
}

// Adapter Class for VLC Player
public class VlcAdapter : IMediaPlayer
{
    private VlcPlayer _vlcPlayer;

    public VlcAdapter(VlcPlayer vlcPlayer)
    {
        _vlcPlayer = vlcPlayer;
    }

    public void Play(string fileName)
    {
        _vlcPlayer.PlayVlc(fileName);  // Adapted to use PlayVlc method
    }
}

// Client Code
public class MediaPlayerClient
{
    private IMediaPlayer _mediaPlayer;

    public MediaPlayerClient(IMediaPlayer mediaPlayer)
    {
        _mediaPlayer = mediaPlayer;
    }

    public void PlayMedia(string fileName)
    {
        _mediaPlayer.Play(fileName);
    }
}

// Usage
class Program
{
    static void Main(string[] args)
    {
        // Playing an MP4 file through the MP4 Adapter
        Mp4Player mp4Player = new Mp4Player();
        IMediaPlayer mp4Adapter = new Mp4Adapter(mp4Player);
        MediaPlayerClient mp4Client = new MediaPlayerClient(mp4Adapter);
        mp4Client.PlayMedia("movie.mp4");

        // Playing a VLC file through the VLC Adapter
        VlcPlayer vlcPlayer = new VlcPlayer();
        IMediaPlayer vlcAdapter = new VlcAdapter(vlcPlayer);
        MediaPlayerClient vlcClient = new MediaPlayerClient(vlcAdapter);
        vlcClient.PlayMedia("documentary.vlc");
    }
}

Explanation of Code

  1. IMediaPlayer Interface - Defines the Play method for playing media files.
  2. Mp4Player & VlcPlayer - Represent third-party players for playing MP4 and VLC formats, each with their own specific methods (PlayMp4 and PlayVlc).
  3. Mp4Adapter & VlcAdapter - Convert (adapt) the incompatible interfaces of Mp4Player and VlcPlayer to the standard IMediaPlayer interface.
  4. MediaPlayerClient - The client that only knows about IMediaPlayer. It can play MP4 or VLC files using the adapters without needing to know the details of how they are played.

Output

Adapter Design Pattern

Real-World Example of Adapter Pattern

Electric Power Socket Adapter - In real life, different countries have different types of power sockets (e.g., Type A, B, C plugs). To use an appliance from one country in another with different sockets, we use a power adapter, which converts the socket type into one that is compatible with the appliance.

Adapter Design Pattern

Here is the illustration that visually represents the concept of a power socket adapter, showing how it converts between different plug types (Type A, B, C) to fit an appliance.

Here's an implementation of the Adapter Design Pattern to represent the real-life example of power sockets and plug types.

Problem

In different countries, there are different types of power sockets (Type A, B, C). To use an appliance (like a charger) from one country in another country with a different socket type, we need an adapter to convert the plug type into one compatible with the socket.

Solution

We'll use the Adapter Design Pattern to adapt an incompatible plug to fit a socket.

Step-by-Step Explanation

  1. Target Interface (ISocket) - This is the interface that defines the standard method for powering appliances, regardless of the socket type.
  2. Adaptee Classes (TypeAPlug, TypeBPlug) - Represent different plug types that are not directly compatible with the socket.
  3. Adapter Classes (TypeAAdapter, TypeBAdapter) - These adapt the plug type to be used with a compatible socket.
  4. Client Code: Uses the ISocket interface to power any appliance using different types of plugs through the adapters.

C# Code Example

using System;

// Target Interface: This represents a standard socket
public interface ISocket
{
    void ProvidePower();
}

// Adaptee Class 1: A Type A plug appliance
public class TypeAPlug
{
    public void InsertTypeAPlug()
    {
        Console.WriteLine("Type A plug inserted.");
    }
}

// Adaptee Class 2: A Type B plug appliance
public class TypeBPlug
{
    public void InsertTypeBPlug()
    {
        Console.WriteLine("Type B plug inserted.");
    }
}

// Adapter Class for Type A Plug
public class TypeAAdapter : ISocket
{
    private TypeAPlug _typeAPlug;

    public TypeAAdapter(TypeAPlug typeAPlug)
    {
        _typeAPlug = typeAPlug;
    }

    public void ProvidePower()
    {
        _typeAPlug.InsertTypeAPlug(); // Adapts the Type A plug to the standard socket
        Console.WriteLine("Power is being provided through Type A adapter.");
    }
}

// Adapter Class for Type B Plug
public class TypeBAdapter : ISocket
{
    private TypeBPlug _typeBPlug;

    public TypeBAdapter(TypeBPlug typeBPlug)
    {
        _typeBPlug = typeBPlug;
    }

    public void ProvidePower()
    {
        _typeBPlug.InsertTypeBPlug(); // Adapts the Type B plug to the standard socket
        Console.WriteLine("Power is being provided through Type B adapter.");
    }
}

// Client Code: The socket expects to power any appliance using the ISocket interface
public class PowerSocketClient
{
    private ISocket _socket;

    public PowerSocketClient(ISocket socket)
    {
        _socket = socket;
    }

    public void PowerAppliance()
    {
        _socket.ProvidePower(); // Powers the appliance using the adapter
    }
}

// Usage Example
class Program
{
    static void Main(string[] args)
    {
        // Using a Type A plug in a standard socket via the Type A Adapter
        TypeAPlug typeAPlug = new TypeAPlug();
        ISocket typeAAdapter = new TypeAAdapter(typeAPlug);
        PowerSocketClient clientA = new PowerSocketClient(typeAAdapter);
        clientA.PowerAppliance(); // Type A plug adapts to the socket

        // Using a Type B plug in a standard socket via the Type B Adapter
        TypeBPlug typeBPlug = new TypeBPlug();
        ISocket typeBAdapter = new TypeBAdapter(typeBPlug);
        PowerSocketClient clientB = new PowerSocketClient(typeBAdapter);
        clientB.PowerAppliance(); // Type B plug adapts to the socket
    }
}

Explanation of Code

  1. ISocket Interface - This is the standard interface for providing power to appliances.
  2. TypeAPlug and TypeBPlug - These represent different types of plugs (Type A and B) that cannot directly connect to the socket.
  3. TypeAAdapter and TypeBAdapter - These adapter classes convert Type A and Type B plugs into a form that can be powered by the socket.
  4. PowerSocketClient - The client that powers any appliance via a standard socket interface (ISocket). The client doesn't need to know about the plug type; it relies on the adapter to handle that.

Output

Adapter Design Pattern

Applications of Adapter Design Pattern

  1. Database Adapter - Used in ORM tools to adapt different databases (e.g., SQL Server, MySQL) to work with a single interface.
  2. GUI Toolkits - Adapting components from different libraries to work within the same GUI framework.
  3. API Wrapping - Adapting different third-party APIs to present a consistent interface to your application.
  4. Legacy Code Wrapping - Encapsulating legacy systems with an adapter to allow newer systems to communicate with them without altering the legacy code.

Prev Next

Top Articles

  1. What is JSON
  2. How to convert a javaScript object in JSON object
  3. Some Important JSON Examples
  4. Common JSON Interview Question