티스토리 뷰


오늘은 어댑터패턴에 대해 포스팅 하겠습니다.




1. Adapter Pattern 이란?

어댑터 패턴(Adapter Pattern)은 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 패턴이로, 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 작동하도록 해 준다 (출처 : 위키백과)


간단하게 예를 들자면, 어댑터패턴은 돼지코입니다.


한국의 전자기기를 외국에서 사용해야 합니다. 물론 같은 국가도 있겠지만, 분명 전원 소켓이 다른 국가가 있고 여기에서 한국에 표준화된 전가기기를 사용하려면 반드시 돼지코가 있어야 합니다. 


어댑터 패턴도 동일합니다. 

기존에 사용하던 클래스가 있습니다. 고객이 제공한 클래스를 무조건 사용해야 한다고 요구를 받았을 때, 기존 클래스와 고객사에서 제공한 클래스와의 가교 역할을 하는 것이 어댑터 클래스입니다. 

이렇게 수정하게 되면 기존 코드, 제공 받은 코드의 수정 없이 어댑터 클래스 구현으로 제공받은 코드의 기능을 기존 코드에서 사용할 수 있게 만들 수 있는 매우 큰 장점이 있습니다.




2. 구현

 1) 객체 어댑터

첫번째, 어댑터를 객체로 관리하는 객체 어댑터입니다.


직접 구현한 코드의 UML을 들고오고 싶지만, 여러모로 상황이 안 되는 관계로 헤드퍼스트 교재의 UML을 들고왔습니다.

클라이언트는 어댑터 클래스를 통해 위 스크린샷의 Adaptee의 동작을 Adapter를 통해 일으킬 수 있는 구조입니다.

 

 2) 코드 구현

예전에 전략 패턴에서 사용했던 오리의 컨셉을 그대로 가지고 와서 구현했습니다.

오리에 관한 회사인데 어떤 고객이 칠면조 클래스를 주면서 관리를 하라고 합니다. 하지만 현재 회사가 가진 솔루션은 오리를 관리하는 솔루션 밖에 없어 칠면조 클래스는 관리를 할 수 없는 안타까운 상황입니다. 그래서 칠면조에 오리 가면을 씌워 관리를 하려 합니다.


<CDuck.h>

1
2
3
4
5
6
class CDuck {
public:
    virtual void Quack() = 0;
    virtual void Fly() = 0;
};
 
cs


오리의 추상 클래스입니다. 오리는 울 수(Quack()) 있고 날 수(Fly()) 있었습니다.


하지만, 칠면조는,

<CTurkey.h>

1
2
3
4
5
class CTurkey {
public:
    virtual void Gobble() = 0;
    virtual void Fly() = 0;
};
cs

꽥꽥거리며 울 수 없는 새입니다. 고블고블 거리면서 울고 있네요. 이 클래스(인터페이스)를 수정하지 않고 그대로 사용해야 하기 때문에, 이를 연결할 수 있는 어댑터 클래스를 만들었습니다.

<CWildTurkey.h>

1
2
3
4
5
6
7
class CWildTurkey : public CTurkey {
 
public:
    CWildTurkey();
    void Gobble();
    void Fly();
};
cs

CTurkey 를 상속받아 구현된 야생의 칠면조 클래스입니다.

<CWildTurkey.cpp>

1
2
3
4
5
6
7
8
9
void CWildTurkey::Gobble()
{
    printf("Gobble Gobble\n");
}
 
void CWildTurkey::Fly()
{
    printf("I'm fly short distance.\n");
}
cs

야생의 칠면조는 고블고블 거리면서 울고, 아주 조금 날 수 있다고 하네요.


<CTurkeyAdapter.h>

1
2
3
4
5
6
7
8
9
10
11
class CTurkeyAdapter : public CDuck{
    
public:
    CTurkey *mTurkey;
 
    CTurkeyAdapter(CTurkey *turkey):
    mTurkey(turkey) {};
 
    void Quack();
    void Fly();
};
cs

어댑터 클래스는 연결해야 하는 인터페이스를 포인터로 가지고 있고, 사용할 인터페이스를 상속받고 있습니다.

 CDuck.h 을 상속받고 있기 때문에, 가상함수로 정의해 놓은 Quack(),  Fly()  에 대한 구현이 필요합니다.

이 Quack(),  Fly() 를 칠면조의 Gobble() , Fly() 에 행동 맵핑을 하게 되면, CDuck의 함수를 통해 칠면조의 행동을 제어할 수 있게 됩니다.


<CTurkeyAdapter.cpp>

1
2
3
4
5
6
7
8
9
void CClassTurkeyAdapter::Quack()
{
    mTurkey->Gobble();
}
 
void CClassTurkeyAdapter::Fly()
{
    mTurkey->Fly();
}
cs

멤버변수로 가지고 있는 Turkey의 행동을 어댑터 클래스에서 할 수 있게 구현하였습니다.


<main.cpp>

1
2
3
4
5
6
7
8
int main() {
    CWildTurkey *turkey = new CWildTurkey();
 
    CTurkeyAdapter *turkeyAdapter = new CTurkeyAdapter(turkey);
 
    turkeyAdapter->Fly();
    turkeyAdapter->Quack();
}
cs

이렇게 구현한 어댑터에, 지금 당장 제어할 수 없는 야생의 칠면조 객체(CWildTurkey)를 어댑터에 끼워맞춰준다면, CDuck의 함수로 칠면조의 행동을 만들 수 있게 됩니다.


<출력>

칠면조를 조금 날리고, 칠면조가 우는 모습을 확인할 수 있습니다.


2) 클래스 어댑터

클래스 어댑터 또한 어댑터 클래스를 통해 제공받은 코드를 기존 코드를 통해 작동시킨다는 맥락은 똑같습니다. 다만 이 어댑터 클래스는 다중상속이 가능해야 구현가능한 패턴입니다. 다행히도 C++은 다중상속을 지원하고 있습니다.


다른 점은 어댑터를 만들 때 사용하고 있는 타겟 클래스와 제공받은 클래스 모두를 상속받는다는 점입니다.

구현시 사용하는 타겟 클래스(오리)는 동일합니다. 하지만 어댑티 클래스(칠면조)의 내용은 구현되어 있어야 합니다. 그래서 칠면조 클래스는 인터페이스가 아닌 실제로 구현되어 있는 CWildTurkey 를 상속받습니다.


<CClassTurkeyAdapter.h>

1
2
3
4
5
6
7
8
9
10
11
class CClassTurkeyAdapter :public CDuck, CWildTurkey {
 
public:
    CClassTurkeyAdapter(CWildTurkey *turkey):
    mTurkey(turkey) {};
    void Quack();
    void Fly();
 
private:
    CWildTurkey *mTurkey;
};
cs

구현부도 위의 객체 어댑터와 동일합니다. 단지 다중상속을 받고, 이미 구현된 클래스를 어댑티 클래스로서 상속을 받는다는 것만 다른 내용입니다.


<main.cpp>

1
2
3
4
5
6
7
8
int main() {
    CWildTurkey *turkey = new CWildTurkey();
    
    CClassTurkeyAdapter *cTurkeyAdapter = new CClassTurkeyAdapter(turkey);
    
    cTurkeyAdapter->Fly();
    cTurkeyAdapter->Quack();
}
cs

메인 함수도 동일한 방법으로 실행을 시키면 위와 동일한 출력이 나오는 걸 확인하실 수 있습니다.




3. 정리

어댑터패턴은 이와 같이 제공받은 클래스를 기존의 코드로 돌려야 할 경우 매우 유용하게 사용할 수 있는 패턴입니다.

다음 포스팅은 퍼사드 패턴으로 찾아 뵙겠습니다. 사실 퍼사드 패턴 보다는, 최소지식원칙에 좀 더 초점을 둔 포스팅이 될 것 같습니다.

혹시 틀린 부분 있으면 언제든 알려주시면 감사합니다.


최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함