C++ Adapter Design Pattern Example

1. Definition

The Adapter Design Pattern allows objects with incompatible interfaces to collaborate. It acts as a bridge between two incompatible interfaces by providing a way to make one interface compatible with another.

2. Problem Statement

Imagine having a class with a specific interface that you need to use in a context expecting a different interface. Direct interaction is not possible due to the interface mismatch.

3. Solution

The Adapter pattern suggests creating a new adapter class that joins the incompatible interfaces. The adapter class contains a reference to an instance of one of the objects and implements the expected interface, thus forwarding calls to the wrapped object.

4. Real-World Use Cases

1. Reading data from different formats (e.g., XML, JSON) and providing a unified API.

2. Integrating legacy code with newer systems.

3. Using third-party libraries with incompatible interfaces in your application.

5. Implementation Steps

1. Identify the existing target interface you want the existing component (adaptee) to collaborate with.

2. Create the adapter class implementing the target interface.

3. In the adapter class, include a reference to the adaptee object.

4. Implement the methods of the target interface in the adapter class, translating requests to the appropriate methods of the adaptee.

6. Implementation in C++

// Target interface
class Target {
public:
    virtual ~Target() {}
    virtual void request() const {
        std::cout << "Target request" << std::endl;
    }
};
// Adaptee: Existing functionality which needs adapting
class Adaptee {
public:
    void specificRequest() const {
        std::cout << "Specific request from Adaptee" << std::endl;
    }
};
// Adapter: Allows Adaptee to be used where Target is expected
class Adapter : public Target {
private:
    Adaptee* adaptee;
public:
    Adapter(Adaptee* a) : adaptee(a) {}
    void request() const override {
        adaptee->specificRequest();
    }
};
int main() {
    Adaptee* adaptee = new Adaptee();
    Target* target = new Adapter(adaptee);
    target->request();
    delete adaptee;
    delete target;
    return 0;
}

Output:

Specific request from Adaptee

Explanation:

1. Target is the interface that the client expects.

2. Adaptee contains the specific functionality, but its interface is not compatible with the client.

3. Adapter is the bridge between Target and Adaptee. It implements the Target interface and has a reference to an Adaptee object. Calls to the request method of the Adapter are forwarded to the specificRequest method of the Adaptee.

4. In the main function, an Adapter object is created wrapping an Adaptee object, allowing the client to use the Adaptee as if it's a Target.

7. When to use?

Use the Adapter pattern when:

1. You need to use existing classes with incompatible interfaces.

2. You want to create a reusable class that works with classes not related through inheritance.

3. You want to adapt a class to multiple interfaces.


Comments