Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Understanding and Implementing the Adapter Pattern in C# and C++

4.78/5 (37 votes)
19 Mar 2012CPOL4 min read 82.6K   1.4K  
Understanding when we could find the Adapter pattern useful and how can we have a rudimentary implementation of the Adapter pattern using C# and C++.

Introduction

This article aims at understanding when we could find the Adapter pattern useful and how can we have a rudimentary implementation of the Adapter pattern using C# and C++.

Background

It was almost 6 years back, at the very beginning of my career, that one of my clients told me to write an adapter. Back then I was unaware of the "Adapter Pattern" but I took his words literally and started my work. I did the required task and wrote an adapter. I think this pattern is something that anyone will find very easy to understand and even implement.

What exactly is an adapter? If I have a source providing me something in some format and my target is expecting that stuff in some other format, then I can hook in a module in between these two guys that will do the conversion. This entity working in between these two is my adapter. I remember the adapter that I used to power up my 16 bit gaming console (expecting 9 volts of power) using a 220 volt power supply.

Adapter pattern article image

The Adapter we are going to talk about here is also on the same lines but it works for classes and objects. If I have a class that is exposing some functions but my client is expecting some other interface, then I can have an adapter in between. The benefit of having an adapter is that the client need not be changed every time I choose to change my underlying object that is being used. I just have to have an adapter written for the new target and the client code will work seamlessly.

Using the Code

GoF defines Adapter pattern as "Convert the interface of a class into another interface that the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces."

Image 2

Let us try to understand this class diagram:

  • Target: Defines the interface that the client uses.
  • Adapter: It uses (adapts) the Adaptee's interface and exposes the Target interface to the client.
  • Adaptee: This is the object whose interface needs to be adapted.
  • Client: It uses the Adaptee functionality via the adapter interface, i.e., Target.

One good example of when we could find this pattern useful is when visualizing an MP3 player application. This MP3 player application was developed on top of some library (say DirectX). The application makes DirectX specific calls to achieve the playback functionality. After some time, there is a requirement of using XNA for playback instead of DirectX. This could mean either rewriting the complete application to change the DirectX specific calls to XNA specific calls, or we could simply write an adapter for XNA in such a way that the client application could work with minimum or no changes.

Let us try to write a small toy application to demonstrate this concept. We will create a small client that will use two libraries (dummy) to perform some operations. These two libraries expose different interfaces so we will write a small adapter to use the libraries. Let us look at the two libraries first, Adaptee:

C#
//Library One
class LibraryOne
{
    public void ThisIsHowOneDoesIt()
    {
        Console.Write("Using Library ONE to perform the action\n");
    }
}

//Library Two
class LibraryTwo
{
    public string ThisIsHowTwoDoesIt()
    {
        return "Using Library TWO to perform the action";
    }
}

Now let us look at the C++ Implementation of these Libraries i.e. Adaptee

C++
//LibraryOne
class LibraryOne
{
public:

	void ThisIsHowOneDoesIt()
	{
	   std::cout<< "Using Library ONE to perform the action\n";
	}
	
};
//LibraryTwo
class LibraryTwo
{
public:

	std::string ThisIsHowTwoDoesIt()
	{
	   return "Using Library TWO to perform the action\n";
	}
};

Now let us write the adapter interface for our concrete adapters, i.e., Target from the above class diagram.

C#
interface IAdapter
{
    void Do();        
}

The C++ Implementation of the Target i.e. IAdapter

C++
class IAdapter
{
public:

	virtual void Do() = 0;
};

and now we will write the concrete Adapter classes to use the two libraries:

C#
//Adapter for first library
class AdapterOne : IAdapter 
{
    private LibraryOne one = null;

    public AdapterOne()
    {
        one = new LibraryOne();
    }

    #region IAdapter Members

    public void Do()
    {
        one.ThisIsHowOneDoesIt();
    }

    #endregion
}
//Adapter for second library
class AdapterTwo : IAdapter 
{
    private LibraryTwo two = null;

    public AdapterTwo()
    {
        two = new LibraryTwo();
    }

    #region IAdapter Members

    public void Do()
    {
        Console.WriteLine(two.ThisIsHowTwoDoesIt() + "\n"); ;
    }

    #endregion
}

The C++ Implementation of Concrete Adapters is

C++
//Adapter to use library one
class AdapterOne : public IAdapter
{
public:
	void Do()
	{
		LibraryOne one;

		one.ThisIsHowOneDoesIt();
	}
};
//Adapter to use library two
class AdapterTwo : public IAdapter
{
public:
	void Do()
	{
		LibraryTwo two;

		std::cout << two.ThisIsHowTwoDoesIt();
	}	
};

So now we have a single interface that the client can use. The respective adapters will take care of making the calls to the respective underlying objects. Let us see how the Client can use this adapter.

C#
static void Main(string[] args)
{
    IAdapter adapter = null;

    //Let emulate the decision where the choice of using the underlying system is made
    Console.WriteLine("Enter which library you wanna use to do operation {1,2}");
    int x = Console.Read();

    if (x == '1')
    {
        //Let us choose to use Library one to do something
        adapter = new AdapterOne();
    }
    else if (x == '2')
    {
        //Let us choose to use Library two to do something
        adapter = new AdapterTwo();
    }

    //Just do the operation now
    adapter.Do();
}

and finally the C++ Implementation of Client

C++
int _tmain(int argc, _TCHAR* argv[])
{
	IAdapter *adapter = 0;
  
        //Let emulate the decision where the choice of using the underlying system is made
	cout << "Enter which library you wanna use to do operation {1,2}";
	int x;
	cin >> x;

	if (x == 1)
	{
	   //Let us choose to use Library one to do something
	  adapter = new AdapterOne();
	}
	else if (x == 2)
	{
	   //Let us choose to use Library two to do something
	   adapter = new AdapterTwo();
	}

	//Just do the operation now
	adapter->Do();

	delete adapter;
	return 0;
};

So now the client can use the same interface to perform operations using both underlying objects. The Adapter pattern is particularly useful when we have two classes that perform similar functions but have different interfaces. The client wants to use both classes, so instead of having conditional code scattered all around the client code, it would be better and simpler to understand if these two classes share the same interface. We cannot change the class interfaces, but we can have an Adapter which allows the client to use a common interface to access the underlying classes.

Before wrapping up, let us look at the class diagram of our small application and compare it with the GoF diagram.

Adapter pattern article image

Points of Interest

This article was written from a beginner's point of view and demonstrated the implementation of the Adapter pattern using a non-realistic example. The Adapter pattern is a really simple but important Design Pattern. The .NET Framework uses this pattern in its core to use the legacy COM component.

History

  • 06 March 2012: First version.
  • 20 March 2012: Added the C++ Implementation for Adapter Pattern.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)