StructuralC++verifiedVerified
Adapter Pattern in C++
Converts the interface of a class into another interface that clients expect, allowing incompatible interfaces to work together.
How to Implement the Adapter Pattern in C++
1Step 1: Define the target interface clients expect
class JsonLogger {
public:
virtual ~JsonLogger() = default;
virtual void logJson(const std::string& json) = 0;
};2Step 2: The adaptee has an incompatible interface
class LegacyLogger {
public:
void writeLog(const std::string& severity, const std::string& message) {
std::cout << "[" << severity << "] " << message << "\n";
}
};3Step 3: Adapter wraps the legacy logger to match the target
class LoggerAdapter : public JsonLogger {
LegacyLogger legacy_;
public:
void logJson(const std::string& json) override {
// Extract fields from JSON (simplified)
legacy_.writeLog("INFO", "Adapted: " + json);
}
};
int main() {
std::unique_ptr<JsonLogger> logger = std::make_unique<LoggerAdapter>();
logger->logJson(R"({"message": "Hello from adapter"})");
}#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <format>
#include <functional>
#include <concepts>
// [step] Define modern payment interface
struct PaymentResult {
bool success;
std::string transactionId;
double amount;
std::string currency;
};
class IPaymentProcessor {
public:
virtual ~IPaymentProcessor() = default;
virtual PaymentResult charge(double amount, const std::string& currency,
const std::string& token) = 0;
virtual PaymentResult refund(const std::string& transactionId,
double amount) = 0;
virtual std::string name() const = 0;
};
// [step] Legacy payment API with different interface
class LegacyPaymentGateway {
public:
struct LegacyResponse {
int code; // 0 = success
std::string ref;
int amountCents;
};
LegacyResponse processPayment(int amountCents, const std::string& cardToken) {
return {0, "LEG-" + cardToken.substr(0, 4), amountCents};
}
LegacyResponse reversePayment(const std::string& ref, int amountCents) {
return {0, "REF-" + ref, amountCents};
}
};
// [step] Adapter with currency conversion and error mapping
class LegacyPaymentAdapter : public IPaymentProcessor {
std::unique_ptr<LegacyPaymentGateway> gateway_;
int toCents(double amount) const {
return static_cast<int>(amount * 100 + 0.5);
}
PaymentResult fromLegacy(const LegacyPaymentGateway::LegacyResponse& resp,
double amount, const std::string& currency) const {
return {
resp.code == 0,
resp.ref,
amount,
currency
};
}
public:
LegacyPaymentAdapter()
: gateway_(std::make_unique<LegacyPaymentGateway>()) {}
PaymentResult charge(double amount, const std::string& currency,
const std::string& token) override {
auto resp = gateway_->processPayment(toCents(amount), token);
return fromLegacy(resp, amount, currency);
}
PaymentResult refund(const std::string& transactionId,
double amount) override {
auto resp = gateway_->reversePayment(transactionId, toCents(amount));
return fromLegacy(resp, amount, "USD");
}
std::string name() const override { return "LegacyPaymentAdapter"; }
};
// [step] Functional adapter using lambdas
template <typename Adaptee>
class FunctionalAdapter : public IPaymentProcessor {
Adaptee adaptee_;
std::string name_;
std::function<PaymentResult(Adaptee&, double, const std::string&,
const std::string&)> chargeFn_;
std::function<PaymentResult(Adaptee&, const std::string&, double)> refundFn_;
public:
FunctionalAdapter(std::string name, Adaptee adaptee,
decltype(chargeFn_) charge, decltype(refundFn_) refund)
: adaptee_(std::move(adaptee)), name_(std::move(name)),
chargeFn_(std::move(charge)), refundFn_(std::move(refund)) {}
PaymentResult charge(double amount, const std::string& currency,
const std::string& token) override {
return chargeFn_(adaptee_, amount, currency, token);
}
PaymentResult refund(const std::string& txId, double amount) override {
return refundFn_(adaptee_, txId, amount);
}
std::string name() const override { return name_; }
};
// [step] Demonstrate both adapter styles
int main() {
// Class adapter
LegacyPaymentAdapter adapter;
auto result = adapter.charge(49.99, "USD", "tok_abc123");
std::cout << std::format("[{}] charge: success={}, txId={}, amount={:.2f} {}\n",
adapter.name(), result.success, result.transactionId,
result.amount, result.currency);
// Functional adapter
LegacyPaymentGateway rawGateway;
auto funcAdapter = std::make_unique<FunctionalAdapter<LegacyPaymentGateway>>(
"FuncAdapter", std::move(rawGateway),
[](LegacyPaymentGateway& gw, double amt, const std::string& cur,
const std::string& tok) -> PaymentResult {
auto r = gw.processPayment(static_cast<int>(amt * 100), tok);
return {r.code == 0, r.ref, amt, cur};
},
[](LegacyPaymentGateway& gw, const std::string& txId,
double amt) -> PaymentResult {
auto r = gw.reversePayment(txId, static_cast<int>(amt * 100));
return {r.code == 0, r.ref, amt, "USD"};
}
);
auto r2 = funcAdapter->charge(29.99, "EUR", "tok_xyz789");
std::cout << std::format("[{}] charge: success={}, txId={}\n",
funcAdapter->name(), r2.success, r2.transactionId);
}Adapter Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Adapter Pattern in the Real World
“A travel power adapter lets your American laptop plug (client) work in a European wall socket (adaptee) without modifying either. The adapter speaks both “languages”, translating the two-pin plug to the two-round-pin socket, making them interoperable without any changes on either side.”