3 분 소요

Adapter는 임의의 개체(Adpatee)를 기존 인터페이스로 사용하기 위해 감쌉니다. 개체의 소스코드를 직접 수정할 수 없을때 사용합니다.

설명

프로젝트를 진행하다 보면, 다른 프로젝트에서 사용했던 클래스를 재활용할 수 있고, 외부 라이브러리에서 제공한 소스코드를 사용할 수도 있습니다.

이런 소스코드들이 현재 프로젝트와 인터페이스가 맞지 않다고 임의로 수정한다면, 다른 프로젝트와 충돌이 있을 수 있습니다. 이를 맞추겠다고 창의적으로 덕트 테이프를 덕지덕지 붙여 해결해봤자 프랑켄코드가 될 뿐입니다.

그렇다고 복제해서 수정한다면, 코드가 중복될 우려도 있고, 버전업 되었을때 복제된 코드에도 일일이 반영해야 하는 문제가 발생합니다. 절대 코드를 복제하지 마세요. 복사 붙여넣기는 코드 중복을 만드는 악마의 유혹입니다.

이러한 경우의 유일한 해결 방법은 AdapteeAdapter로 감싸서 인터페이스를 맞추는 겁니다.

image

클래스 Adpater와 개체 Adapter

Adapter는 구현 방법에 따라 클래스 Adpater개체 Adapter로 구분할 수 있습니다.

클래스 Adpater

클래스 Adpaterprivate 상속을 통해 Adaptee를 포함합니다. 다중 상속을 하며 Request()호출시 Adaptee::SpecificRequest()를 호출합니다.

Adapter

개체 Adapter

개체 Adpater멤버 변수Adaptee를 포함합니다. Request()호출시 m_Adaptee.SpecificRequest()를 호출합니다.

개체 Adapter의 경우 멤버 변수를 런타임에 변경할 수 있어 다른 개체로 교체 가능하며, Adaptee상속한 개체도 사용할 수 있어 확장성이 좋습니다.

Adapter

항목 내용
Target 현재 시스템에서 사용중인 인터페이스입니다.
Adaptee Adapter로 감싼 기존 개체입니다.
Adapter Target인터페이스로 Adaptee를 사용할 수 있게 하는 개체입니다.
Client Target인터페이스를 필요로하는 개체입니다.

특징

Adpater는 소스코드를 수정할 수 없는 상황에서만 유용합니다. 예를 들면 Adaptee가 다른 프로젝트에서도 사용되는 코드여서 함부로 수정하면 안되거나, 소스코드 없이 제공되는 외부 라이브러리인 경우 처럼요.

만약, 수정가능한 코드인데 괜히 Adapter로 감싼다면 잘못된 설계입니다. 캘린더 코더 일 수 있죠. 패턴 중독에 빠지지 마세요.

예제

다음은 Shape상속한 개체들을 출력하는 예입니다. ShapeDraw()함수를 이용하는데요, Circle 개체는 Shape상속하지 않은 클래스여서 #5와 같이 사용할 수 없습니다.

CircleShape상속하도록 리팩토링하면 될일입니다만, 만약 다른 프로젝트에서도 사용되는 코드라면 함부로 수정해선 안됩니다. 다른 프로젝트에 영향을 주니까요. 혹은 소스코드 없이 라이브러리로 제공되는 것이라면, 아예 수정할 수 없죠.

이럴때 Apdater를 이용하여 감싸면 Shape처럼 사용할 수 있습니다.

  1. #1 : ShapeDraw()를 제공하는 부모 클래스입니다.
  2. #2 : RectangleEllipseShape상속하여 Draw()를 제공합니다.
  3. #3 : CircleShape상속하지 않아 Draw()를 제공하지는 않지만, 동일한 기능인 Render()를 제공합니다.
  4. #4 : 클래스 Adapter개체 Adapter방식으로 Circle개체에서 Shape인터페이스를 제공합니다.
  5. #5 : CircleShape이 아니므로 std::vector<std::unique_ptr<Shape>>에 추가할 수 없습니다.
  6. #6 : Adapter를 이용하여 Circlestd::vector<std::unique_ptr<Shape>>에 추가하여 사용할 수 있습니다.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// ----
// #1. Draw()를 제공하는 부모 클래스입니다.
// ----
class Shape {
protected:
    Shape() = default; // 다형 소멸을 제공하는 추상 클래스. 상속해서만 사용하도록 protected
public:
    virtual ~Shape() = default; // 다형 소멸 하도록 public virtual   
private:
    Shape(const Shape&) = delete; 
    Shape(Shape&&) = delete; 
    Shape& operator =(const Shape&) = delete; 
    Shape& operator =(Shape&&) = delete;   
public:
    virtual void Draw() const = 0; // #1
};
// ----
// #2. Shape을 상속하여 Draw()를 제공합니다.
// ----   
class Rectangle : public Shape {
public:
    Rectangle() = default;

    virtual void Draw() const override { // #2
        std::cout << "Rectangle::Draw()" << std::endl;
    }
};
class Ellipse : public Shape {
public:
    Ellipse() = default;

    virtual void Draw() const override { // #2
        std::cout << "Ellipse::Draw()" << std::endl;
    }
};
// ----
// #3. Shape을 상속하지 않아 Draw()를 제공하지는 않지만, 동일한 기능인 Render()를 제공합니다.
// ----  
class Circle { // Shape을 상속하지 않았습니다.
public:
    Circle() = default;

    void Render() const { // #3
        std::cout << "Circle::Render()" << std::endl;            
    }
};  

// ----
// #4. 클래스 Adapter. Circle 클래스를 Shape 처럼 사용할 수 있게 해줍니다.
// ----
class CircleClassAdapter : public Shape, private Circle {
public:
    CircleClassAdapter() = default;

    virtual void Draw() const override { // #4
        Render();
    }   
};

// ----
// #4. 개체 Adapter. Circle 개체를 Shape처럼 사용할 수 있게 해줍니다.
// ----
class CircleObjectAdapter : public Shape {
private:
    std::unique_ptr<Circle> m_Circle;
public:
    explicit CircleObjectAdapter(std::unique_ptr<Circle> circle) : m_Circle(std::move(circle)) {
        assert(m_Circle);
    }
    virtual void Draw() const override { // #4
        assert(m_Circle);
        m_Circle->Render();
    } 
};

// ----
// 테스트 코드
// ----
std::vector<std::unique_ptr<Shape>> v;
v.emplace_back(new Rectangle{}); 
v.emplace_back(new Ellipse{});
// v.emplace_back(new Circle{}); // (X) #5. 컴파일 오류. Circle은 Shape이 아닙니다.
v.emplace_back(new CircleClassAdapter{}); // #6
v.emplace_back(new CircleObjectAdapter{std::unique_ptr<Circle>{new Circle{}}}); // #6

for (auto& shape : v) {
    shape->Draw();
}

댓글남기기