어댑터 패턴(Adapter Pattern)

  • 어댑터 패턴은 코드를 재사용하기 위한 인터페이스를 처리하는 패턴입니다.
  • 또 다른 말로는 래퍼 패턴(Wrapper Pattern)이라고 합니다.
  • 기존의 코드가 새로 변경된 인터페이스와 일치하지 않는 경우가 발생할 수 있는데, 어댑터 패턴은 기존의 코드를 래핑하고 변경된 인터페이스에 맞게 재설계합니다.
  • 어댑터 패턴의 주요 용어
    • 어댑터: 변환을 처리하는 객체
    • 어댑티: 변환을 받아 사용하는 객체

어댑터 패턴의 종류

  • 어댑터 패턴은 두 가지로 구분합니다.
    • 클래스 어댑터: 상속을 사용하여 객체 확장
      • 클래스 어댑터를 구현하기 위해 다중 상속을 활용
      • 최신 언어에서는 다중 상속을 지원하지 않는 경우가 많다.
      • 클래스 어댑터의 장/단점
        • 장점: 별도의 어댑티를 작성하지 않아도 되며, 하나의 클래스로 어댑터 객체를 처리한다.
        • 단점: 여러 개의 클래스를 상속하므로 클래스 사이에 강한 결합이 발생한다.
    • 객체 어댑터: 합성을 사용하여 객체 확장
      • 내부적으로 객체를 재구성한다.
      • 기존 객체는 복합 객체로 사용된다.
      • 객체 어댑터의 장/단점
        • 장점: 복합 객체로 구성하기 때문에 클래스 간 결합이 유연하다.
        • 단점: 어댑터가 새로운 메서드를 재구성할 때 추가적인 코드가 필요하다.

어댑터 패턴: 기존 객체

  • 사용 중이던 기존 객체를 작성합니다.
  • 이 객체는 float의 데이터 타입을 인자로 받고, 반환합니다.
  • 따라서 다른 데이터 타입이 전달되면 정상적인 동작을 기대하기 힘듭니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Math
{
public:
    float twoTime(float num)
    {
        return num * 2;
    }

    float halfTime(float num)
    {
        return num / 2;
    }
};

어댑터 패턴: 클래스 선언

  • 어댑터는 인터페이스입니다.
  • 순수한 가상 메서드로 구성된 클래스로 선언합니다.
  • 기존 객체에서 사용한 데이터 타입(float)이 새롭게 변경된 인터페이스 데이터 타입(int)로 변경되었습니다.
1
2
3
4
5
6
class Adapter
{
public:
    virtual int twoTime(int num) = 0;
    virtual int halfTime(int num) = 0;
};

어댑터 패턴: 클래스 구현

  • 어댑터 인터페이스 클래스를 상속하여 구체적인 구현을 작성합니다.
  • 기존 객체인 Math 객체는 Objects 클래스가 내부적으로 포함하고 있습니다.
  • 기존 객체를 재사용하기 위해 데이터 타입을 변환하고 활용합니다.
 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
class Objects :
    public Adapter
{
public:
    Objects()
    {
        m_adapter = std::make_shared<Math>();
    }

    int twoTime(int num)
    {
        float _num = m_adapter->twoTime(static_cast<float>(num));
        return static_cast<int>(_num);
    }

    int halfTime(int num)
    {
        float _num = m_adapter->halfTime(static_cast<float>(num));
        return static_cast<int>(_num);
    }


private:
    std::shared_ptr<Math> m_adapter;
};

어댑터 패턴: 실행 코드

  • 어댑터 패턴은 구조 패턴 중에서 가장 간단한 패턴입니다.
  • 어댑터 패턴을 사용하면 기존 객체의 클래스와 메서드를 수정하지 않고 원하는 대로 재사용할 수 있습니다.
  • Objects 클래스를 다르게 작성하여 원하는 대로 변경이 가능합니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int main(const int argc, const char* argv[])
{
    std::shared_ptr<Objects> object = std::make_shared<Objects>();
    int numA = object->twoTime(5);
    float numB = object->twoTime(4.0f);
    
    int numC = object->halfTime(5);
    float numD = object->halfTime(4.0f);

    return 0;
}