DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • The Power of Refactoring: Extracting Interfaces for Flexible Code
  • Composite Design Pattern in Java
  • Iterator Design Pattern In Java
  • Security by Design: Building Full-Stack Applications With DevSecOps

Trending

  • Apple and Anthropic Partner on AI-Powered Vibe-Coding Tool – Public Release TBD
  • Efficient API Communication With Spring WebClient
  • How To Introduce a New API Quickly Using Quarkus and ChatGPT
  • Introducing Graph Concepts in Java With Eclipse JNoSQL

Adapter Design Pattern in Modern C++

By 
Vishal Chovatiya user avatar
Vishal Chovatiya
·
Aug. 25, 20 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
13.3K Views

Join the DZone community and get the full member experience.

Join For Free

In software engineering, Structural Design Patterns deal with the relationship between object and classes i.e. how objects and classes interact or build a relationship in a manner suitable to the situation. The structural design patterns simplify the structure by identifying relationships. In this article of the Structural Design Patterns, we're going to take a look at Adapter Design Pattern in Modern C++ which used to convert the interface of an existing class into another interface that client/API-user expect. Adapter Design Pattern makes classes work together that could not otherwise because of incompatible interfaces.

By the way, If you haven't check out my other articles on Structural Design Patterns, then here is the list:

  1.  Adapter
  2.  Bridge
  3.  Composite
  4.  Decorator
  5.  Facade
  6.  Flyweight
  7.  Proxy

The code snippets you see throughout this series of articles are simplified not sophisticated. So you often see me not using keywords like override, final, public(while inheritance) just to make code compact & consumable(most of the time) in single standard screen size. I also prefer struct instead of class just to save line by not writing "public:" sometimes and also miss virtual destructor, constructor, copy constructor, prefix std::, deleting dynamic memory, intentionally. I also consider myself a pragmatic person who wants to convey an idea in the simplest way possible rather than the standard way or using Jargons.

Note: If you stumbled here directly, then I would suggest you go through What is design pattern? first, even if it is trivial. I believe it will encourage you to explore more on this topic. All of this code you encounter in this series of articles are compiled using C++20(though I have used Modern C++ features up to C++17 in most cases). So if you don't have access to the latest compiler you can use https://wandbox.org/ which has preinstalled boost library as well.

Intent

To get the interface you want from the interface you have.

An adapter allows two incompatible classes to work together by converting the interface of one class into an interface expected by the client/API-user without changing them. Basically, adding intermediate class i.e. Adapter.
 
If you find yourself in a situation of using Adapter then you might be working on compatibility between libraries, modules, plugins, etc. If not then you might have serious design issues because, if you have followed Dependency Inversion Principle early in the design. Use of Adapter Design Pattern won't be the case.

Adapter Design Pattern Examples in C++

Implementing an Adapter Design Pattern is easy, just determine the API you have & the API you need. Create a component which aggregates(has a reference to,...) the adaptee.

Classical Adapter

C++
 




x
32


 
1
struct Point {
2
    int32_t     m_x;
3
    virtual void draw(){ cout<<"Point\n"; }
4
};
5

           
6
struct Point2D : Point {
7
    int32_t     m_y;
8
    void draw(){ cout<<"Point2D\n"; }
9
};
10

           
11
void draw_point(Point &p) {
12
    p.draw();
13
}
14

           
15
struct Line {
16
    Point2D     m_start;
17
    Point2D     m_end;
18
    void draw(){ cout<<"Line\n"; }
19
};
20

           
21
struct LineAdapter : Point {
22
    Line&       m_line;
23
    LineAdapter(Line &line) : m_line(line) {}
24
    void draw(){ m_line.draw(); }
25
};
26

           
27
int main() {
28
    Line l;
29
    LineAdapter lineAdapter(l);
30
    draw_point(lineAdapter);
31
    return EXIT_SUCCESS;
32
}



You can also create a generic adapter by leveraging C++ template as follows:

C++
 




xxxxxxxxxx
1


 
1
template<class T>
2
struct GenericLineAdapter : Point {
3
    T&      m_line;
4
    GenericLineAdapter(T &line) : m_line(line) {}
5
    void draw(){ m_line.draw(); }
6
};



The usefulness of the generic approach hopefully becomes more apparent when you consider that when you need to make other things Point-like, the non-generic approach becomes quickly very redundant.

Pluggable Adapter Design Pattern using Modern C++

The Adapter should support the adaptees(which are unrelated and have different interfaces) using the same old target interface known to the client/API-user. Below example satisfy this property by using C++11's lambda function & functional header.
C++
 




xxxxxxxxxx
1
36


 
1
/* Legacy code -------------------------------------------------------------- */
2
struct Beverage {
3
    virtual void getBeverage() = 0;
4
};
5

           
6
struct CoffeeMaker : Beverage {
7
    void Brew() { cout << "brewing coffee" << endl;}
8
    void getBeverage() { Brew(); }
9
};
10

           
11
void make_drink(Beverage &drink){
12
    drink.getBeverage();                // Interface already shipped & known to client
13
}
14
/* --------------------------------------------------------------------------- */
15

           
16
struct JuiceMaker {                     // Introduced later on
17
    void Squeeze() { cout << "making Juice" << endl; }
18
};
19

           
20
struct Adapter : Beverage {              // Making things compatible
21
    function<void()>    m_request;
22

           
23
    Adapter(CoffeeMaker* cm) { m_request = [cm] ( ) { cm->Brew(); }; }
24
    Adapter(JuiceMaker* jm) { m_request = [jm] ( ) { jm->Squeeze(); }; }
25

           
26
    void getBeverage() { m_request(); }
27
};
28

           
29
int main() {
30
    Adapter adp1(new CoffeeMaker());
31
    make_drink(adp1);
32

           
33
    Adapter adp2(new JuiceMaker());
34
    make_drink(adp2);
35
    return EXIT_SUCCESS;
36
}



The pluggable adapter sorts out which object is being plugged in at the time. Once an object has been plugged in and its methods have been assigned to the delegate objects(i.e. m_request in our case), the association lasts until another set of methods is assigned.

What characterizes a pluggable adapter is that it will have constructors for each of the types that it adapts. In each of them, it does the delegate assignments (one, or more than one if there are further methods for rerouting).

Pluggable adapter provides the following two main benefits:
  1.  You can bind an interface(bypassing lambda function in constructor argument), unlike the object we did in the above example.
  2.  This also helps when adapter & adaptee have a different number of the argument.

Benefits of Adapter Design Pattern

  1.  Open-Closed Principle: One advantage of the Adapter Pattern is that you don't need to change the existing class or interface. By introducing a new class, which acts as an adapter between the interface and the class, you avoid any changes to the existing code.
  2.  This also limits the scope of your changes to your software component and avoids any changes and side-effects in other components or applications.
  3.  By above two-point i.e. separate class(i.e. Single Responsibility Principle) for special functionality & fewer side-effects, it's obvious we do requires less maintenance, learning curve & testing.
  4.  AdapterDesing Pattern also adheres to the Dependency Inversion Principle, due to which you can preserve binary compatibility between multiple releases.

Summary by FAQs

When to use the Adapter Design Pattern?

-- Use the Adapter class when you want to use some existing class, but its interface isn't compatible with the rest of your code.
-- When you want to reuse several existing subclasses that lack some common functionality that can't be added to the superclass.
-- For example, let say you have a function which accepts weather object & prints temperature in Celsius. But now you need to print the temperature in Fahrenheit. In this case of an incompatible situation, you can employ the Adapter Design Pattern.

Real-life & practical example of the Adapter Design Pattern?

-- In STL, stack, queue & priority_queue are adaptors from deque & vector. When stack executes stack::push(), the underlying vector does vector::push_back().
-- A card reader which acts as an adapter between the memory card and a laptop.
-- Your mobile & laptop charges are kind of adapter which converts standard voltage & current to the required one for your device.

What are the differences between Bridge & Adapter Design Pattern?

-- Adapter is commonly used with an existing app to make some otherwise-incompatible classes work together nicely.
-- Bridge is usually designed up-front, letting you develop parts of an application independently of each other.

What is the difference between Decorator & Adapter Design Pattern?

-- Adapter converts one interface to another, without adding additional functionalities\
-- Decorator adds new functionality into an existing interface.

What is the difference between Proxy & Adapter Design Pattern?

-- Adapter Design Pattern translates the interface for one class into a compatible but different interface.
-- Proxy provides the same but easy interface or some time act as the only wrapper.

Design Interface (computing)

Published at DZone with permission of Vishal Chovatiya. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • The Power of Refactoring: Extracting Interfaces for Flexible Code
  • Composite Design Pattern in Java
  • Iterator Design Pattern In Java
  • Security by Design: Building Full-Stack Applications With DevSecOps

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!