기존 클래스의 인터페이스를 클라이언트가 원하는 다른 인터페이스로 변환하여, 호환되지 않는 클래스들이 함께 동작할 수 있도록 하는 구조 패턴
- Target : IMonster
- Client : void MonsterAttack(IMonster* _monster, int _damage)
- Adaptee : LegacyMonster
- Adapter : MonsterAdapter
#include <iostream> #include <string> // 기존에 존재하던, 인터페이스가 다른 레거시 클래스 (Adaptee) class LegacyMonster { private: std::string name; public: LegacyMonster(const std::string& _name) : name(_name) {} void Attack_v1(int _damage) { std::cout << name << " - 공격 : " << _damage << std::endl; } }; // 클라이언트가 요구하는 표준 인터페이스 (Target Interface) class IMonster { public: virtual ~IMonster() = default; virtual void Attack(int _damage) = 0; }; // 표준 인터페이스를 따르는 최신 클래스 class ModernMonster : public IMonster { private: std::string name; public: ModernMonster(const std::string& _name) : name(_name) {} void Attack(int _damage) override { std::cout << name << " - 공격 : " << _damage << std::endl; } }; // 레거시 클래스를 표준 인터페이스처럼 보이게 만들어주는 어댑터 클래스 (Adapter) class MonsterAdapter : public IMonster { private: std::unique_ptr<LegacyMonster> legacy; public: MonsterAdapter(std::unique_ptr<LegacyMonster> _legacy = nullptr) : legacy(std::move(_legacy)) {} bool connect(std::unique_ptr<LegacyMonster> _legacy) { if (legacy != nullptr) { return false; } else { legacy = std::move(_legacy); return true; } } void release() { legacy.reset(); } // 표준 Attack() 호출을 받아서 내부의 LegacyMonster의 Attack_v1()으로 변환하여 호출 void Attack(int _damage) override { if (legacy != nullptr) { legacy->Attack_v1(_damage); } else { std::cout << "연결된 몬스터가 없습니다." << std::endl; } } }; // 클라이언트 코드: IMonster 인터페이스를 사용하는 모든 객체를 처리할 수 있음 void MonsterAttack(IMonster* _monster, int _damage) { _monster->Attack(_damage); } int main() { ModernMonster newmonster("뉴몬스터"); MonsterAdapter adapter(std::make_unique<LegacyMonster>("구몬스터")); // MonsterAttack 함수는 newmonster와 adapter의 내부 구현 차이를 모르고 // 오직 IMonster 인터페이스에만 의존하여 동일하게 처리함 MonsterAttack(&adapter, 100); MonsterAttack(&newmonster, 100); return 0; }
- 인터페이스 불일치 문제 : LegacyMonster와 ModernMonster는 서로 다른 공격 메서드(Attack_v1 vs Attack)를 가지고 있다.
- 어댑터의 역할 : MonsterAdapter는 IMonster 인터페이스를 상속받아 클라이언트(MonsterAttack 함수)의 요구사항을 만족시킨다. 그리고 내부적으로는 LegacyMonster 객체를 가지고 있다가, 자신의 Attack 메서드가 호출되면 LegacyMonster의 Attack_v1 메서드를 대신 호출
- 클라이언트 코드의 일관성 : 어댑터 덕분에 main 함수에서는 구형 몬스터든 신형 몬스터든 구분할 필요 없이, MonsterAttack 함수를 통해 일관된 방식으로 다룰 수 있다.