Adapter Design Pattern

Do not miss this exclusive book on Binary Tree Problems. Get it now for free.

The adapter pattern is a software design pattern (also known as wrapper, an alternative naming shared with the decorator pattern) that allows the interface of an existing class (in my implementation I call it Adaptee) to be used as another interface (I call it Target). It is often used to make existing classes work with others without modifying their source code. In this article at OpenGenus, we show two ways to implement the Adapter design pattern.

Table of contents:

  1. Motivation (why do we need adapters)?
  2. The adapter pattern defined
  3. C++ Implementation
  4. Usage of the supplied code

Motivation (why do we need adapters)?

Here is a problem statement that shall demonstrate the need to use object-oriented adapters:

Say you have got an existing software system that you need to work a new vendor class library into, but the new vendor designed their interface differently than the last vendor.

You have 3 solutions to this problem:

  1. Change your existing code (which will probably require lots of other compliance-driven changes).
  2. Change the last vendor’s code (which you probably won’t be able to).
  3. Write a class that adapts the new vendor’s interface into the one you are expecting. That is the Adapter Design Pattern; it is about writing a new code without going through any code change in the existing code.

The Adapter pattern defined

Adapters convert the interface of a class into another interface the clients expect. Adapters lets classes work together that couldn’t otherwise because of incompatible interfaces. The adapter acts as the middleman by receiving requests from the client (i.e. the main() method) and converting them into requests that make sense on the vendor classes. Adapters can therefore be supplied by vendors.

C++ Implementation

Scenario A

The adapter inherits from both; the Target and Adaptee.

#include <iostream>

using namespace std;

// Desired interface (Target)
class Target
{
    public:
    virtual void* function() = 0;
};

// Operative component (Adaptee)
class Adaptee
{
    public:
    Adaptee() {}
    void* adapteeFunction(void*) {
        std::cout << "consuming an adaptee function.\n";
        return NULL;
    }
};

// Adapter wrapper
class Adapter: public Target, private Adaptee
{
    public:
    Adapter():Adaptee() {
        std::cout << "Creating Adapter\n";
    }
    void* function() {
        std::cout << "Target function's job is done by ";
        void* p;
        /*The Adapter translates the client's request into a
        call on the Adaptee using the Adaptee interface*/
        return adapteeFunction(p);
    }
};

int main()
{
    Target* r = new Adapter();

    /*The client makes a request to the Adapter by calling 
    a method on it using the Target interface*/
    
    /*The client receives the results of the call and 
    never knows there is an adapter doing the translation*/
    void* outcome = r->function();
    return 0;
}

Scenario B

Here, composition is favoured over inheritance.

#include <iostream>

using namespace std;

// Desired interface (Target)
class Target
{
    public:
    virtual void* function() = 0;
};

// Operative component (Adaptee)
class Adaptee
{
    public:
    Adaptee() {}
    void* adapteeFunction(void*) {
        std::cout << "consuming an adaptee function.\n";
        return NULL;
    }
};

// Adapter wrapper
class Adapter: public Target
{
    private:
    Adaptee* pAdaptee;
    public:
    Adapter(){
        pAdaptee = new Adaptee();
        std::cout << "Constructing an Adapter which includes constructing an Adaptee as well\n";
    }
    void* function() {
        std::cout << "Target function's job is done by ";
        void* p;
        /*The Adapter translates the client's request into a
        call on the Adaptee using the Adaptee interface*/
        return pAdaptee->adapteeFunction(p);
    }
};

int main()
{
    Target* r = new Adapter();

    /*The client makes a request to the Adapter by calling 
    a method on it using the Target interface*/
    
    /*The client receives the results of the call and 
    never knows there is an adapter doing the translation*/
    void* outcome = r->function();
    return 0;
}

Usage of the supplied code

The code snippets provided here are architectural; to demonstrate the pattern. In order to use this code you will probably need to do some adjustments. You can either adopt its style and build your own implementation. Or you can type-cast the void* arguments and/or functions to fit whatever types suitable to your application.

Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.