어댑터 디자인 패턴은 인터페이스를 표준화하고 기능을 기존 상속 계통에 비간섭적으로 추가하도록 돕는데 중점을 둔다.
의도: ' 클래스 인터페이스를 클라이언트가 기대하는 다른 인터페이스로 변환한다. 어댑터를 사용하면 호환되지 않는 인터페이스 때문에 함께 작동할 수 없는 클래스를 함께 작동하게 한다.'
기존에 이미 구현되어 있는 Document interface가 있다고 하자.
새로운 라이브러리인 Pages를 도입해야 하며, "Document interface와 호환 되어야 한다" 라는 요구사항이 있다고 하면,
위 다이아그램과 같이 "객체 어댑터" 를 사용하여 요구사항을 만족 시킨다.
어댑터 패턴을 활용해 기존 Pages 클래스를 수정하지 않고 매우 쉽게 통합할 수 있다.
아래 Document 클래스를 보자.
class Document
{
public:
virtual ~Document() = default;
virtual int exportToJSON() const = 0;
virtual int serialize() const = 0;
};
Document 클래스에서는 exportToJSON, serialize라는 인터페이스 상속받아 구현해야 한다. 하지만 이번에 추가하는 Pages 클래스는 아래와 같이 전혀 다른 interface를 사용하고 있다. (하지만 기능은 Document 클래스와 같다.)
class Pages
{
public:
virtual void convertToBytes() const = 0;
virtual void convertToJson() const = 0;
virtual ~Pages() = default;
};
새로 도입할 Pages 클래스와 기존 Document 클래스를 호환하기 위해서 다음과 같이 2개의 클래스를 연결해주는 PageAdapter 클래스를 도입한다.
class PageAdapter : public Document
{
public:
explicit PageAdapter(std::unique_ptr<Pages> pages) {
this->concretePages = std::move(pages);
}
//...
int exportToJSON() const override {
concretePages->convertToJson();
return 0;
}
int serialize() const override {
concretePages->convertToBytes();
return 0;
}
private:
std::unique_ptr<Pages> concretePages;
};
위 Adapter패턴을 통해 아래와 같이 Pages 클래스도 Document 인터페이스로 사용할 수 있고, 기존에 이미 Document 로 코딩이 되어 있다면, 결합 하여 사용할 수 있다.
int main()
{
std::unique_ptr<Book> novelBooks = std::make_unique<Book>(328);
PageAdapter documentFolder(std::move(novelBooks));
documentFolder.exportToJSON();
documentFolder.serialize();
}
어댑터 패턴은 사로 다른 인터페이스를 결합하는 패턴으로 이패턴을 적용할 때 기대 행위를 고려하고 LSP 위반을 확인하는 것이 매우 중요하다.
- 어댑터 디자인 패턴은 호환되지 않는 조각이 함께 작동할 수 있게 인터페이스를 조정하는 의도로 적용한다.
- 어댑터는 동적과 정적 다향성 모두에 유용하다는 것을 인식한다.
- 객체 어댑터, 클래스 어댑터, 함수 어댑터를 구별한다.
- 어댑터와 전략 디자인 패턴 간 차이를 이해한다.
- 어댑터 디자인 패턴을 사용할 때는 LSP 위반에 주의를 기울인다.
'Language > C++ Design Pattern' 카테고리의 다른 글
Bridge 패턴 (0) | 2025.06.26 |
---|---|
Command 패턴 (0) | 2025.02.12 |
Strategy 패턴 (0) | 2025.02.09 |
Visitor 패턴 (std::variant) (0) | 2025.02.05 |
Visitor 패턴 (1) | 2025.02.02 |