C++ Adapter 패턴

호환되지 않는 인터페이스를 변환하여 함께 동작할 수 있게 합니다. 클래스 어댑터와 객체 어댑터 방식을 모두 포함합니다.

Gist
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <stdexcept>

// ──────────────────────────────────────────────
// 목표 인터페이스: JSON 데이터 소스
// ──────────────────────────────────────────────
class JsonDataSource {
public:
    virtual ~JsonDataSource() = default;
    virtual std::string fetchJson(const std::string& key) const = 0;
    virtual void pushJson(const std::string& key, const std::string& json) = 0;
};

// ──────────────────────────────────────────────
// 레거시 시스템: XML 기반 API (변경 불가)
// ──────────────────────────────────────────────
class LegacyXmlService {
public:
    std::string getXmlRecord(const std::string& id) const {
        // 실제로는 XML DB 쿼리 수행
        return "<record><id>" + id + "</id><value>legacy_data</value></record>";
    }

    void saveXmlRecord(const std::string& id, const std::string& xml) {
        std::cout << "[LegacyXmlService] XML 저장: id=" << id
                  << ", data=" << xml << "\n";
    }

    // XML → 단순 값 추출 (실제에선 파서 사용)
    static std::string extractValue(const std::string& xml) {
        const std::string tag = "<value>";
        auto start = xml.find(tag);
        if (start == std::string::npos) return "";
        start += tag.size();
        auto end = xml.find("</value>", start);
        return xml.substr(start, end - start);
    }
};

// ──────────────────────────────────────────────
// [객체 어댑터] 컴포지션 방식
// LegacyXmlService를 JsonDataSource 인터페이스로 적응
// ──────────────────────────────────────────────
class XmlToJsonAdapter : public JsonDataSource {
public:
    explicit XmlToJsonAdapter(std::shared_ptr<LegacyXmlService> service)
        : xmlService_(std::move(service)) {}

    // XML 레코드를 가져와 JSON 형태로 변환
    std::string fetchJson(const std::string& key) const override {
        const auto xml = xmlService_->getXmlRecord(key);
        const auto value = LegacyXmlService::extractValue(xml);
        // 단순 JSON 직렬화 (실제에선 nlohmann/json 등 사용)
        return R"({"key":")" + key + R"(","value":")" + value + R"("})";
    }

    // JSON을 파싱해 XML로 변환 후 레거시 서비스에 저장
    void pushJson(const std::string& key, const std::string& json) override {
        // 단순 추출 (실제에선 JSON 파서 사용)
        const std::string xml =
            "<record><id>" + key + "</id><value>" + json + "</value></record>";
        xmlService_->saveXmlRecord(key, xml);
    }

private:
    std::shared_ptr<LegacyXmlService> xmlService_;
};

// ──────────────────────────────────────────────
// [클래스 어댑터] 다중 상속 방식 (대안)
// 인터페이스와 구현 클래스 모두를 상속
// ──────────────────────────────────────────────
class XmlToJsonClassAdapter : public JsonDataSource, private LegacyXmlService {
public:
    std::string fetchJson(const std::string& key) const override {
        const auto xml = getXmlRecord(key);
        const auto value = extractValue(xml);
        return R"({"key":")" + key + R"(","value":")" + value + R"("})";
    }

    void pushJson(const std::string& key, const std::string& json) override {
        const std::string xml =
            "<record><id>" + key + "</id><value>" + json + "</value></record>";
        saveXmlRecord(key, xml);
    }
};

// ──────────────────────────────────────────────
// 클라이언트: JsonDataSource 인터페이스만 알고 있음
// ──────────────────────────────────────────────
void clientCode(JsonDataSource& source) {
    source.pushJson("user_001", R"({"name":"홍길동","role":"admin"})");
    const auto result = source.fetchJson("user_001");
    std::cout << "[클라이언트] 수신한 JSON: " << result << "\n";
}

// ──────────────────────────────────────────────
// 사용 예시
// ──────────────────────────────────────────────
int main() {
    std::cout << "=== 객체 어댑터 (컴포지션) ===\n";
    auto legacyService = std::make_shared<LegacyXmlService>();
    XmlToJsonAdapter objectAdapter(legacyService);
    clientCode(objectAdapter);

    std::cout << "\n=== 클래스 어댑터 (다중 상속) ===\n";
    XmlToJsonClassAdapter classAdapter;
    clientCode(classAdapter);

    return 0;
}