구현 시나리오
다음과 같이 Rect, Circle의 구성으로 도형을 그리는 기능이 이미 구현되어있다. 그리고 도형 외에 글자를 출력할 수 있는 TextView도 준비되어 있다.
#include <iostream>
#include <vector>
#include "TextView.cpp"
using namespace std;
class Shape
{
public:
virtual void Draw() { cout << "Draw Shape" << endl; }
};
class Rect : public Shape
{
public:
virtual void Draw() { cout << "Draw Rect" << endl; }
};
class Circle : public Shape
{
public:
virtual void Draw() { cout << "Draw Circle" << endl; }
};
int main()
{
vector<Shape*> v;
v.push_back(new Rect);
v.push_back(new Circle);
// Text도 추가 해보자!
for (auto p : v)
{
p->Draw();
}
}
#include <string>
#include <iostream>
class TextView
{
std::string data;
std::string font;
int width;
public:
TextView(std::string s, std::string fo = "나눔고딕", int w = 24 ) : data(s), font(fo), width(w) {}
void Show() { std::cout << data << std::endl; }
};
Shape-Rect, Shape-Circle의 구성으로 Shape-Text를 추가하려고 한다. 그런데 Rect, Circle의 멤버 함수 이름은 Draw이지만 TextView의 출력 함수는 show이다. Shape 클래스로 파생되어야 하고, Draw함수에서 그림(글자)를 출력해야 한다. TextView의 Show와 Shape의 Draw 함수 이름의 차이를 어떻게 극복 할 수 있을까?
다음과 같이 간단히 다중 상속으로 구현한다.
// 다중상속
// TextView를 도형편집기에서 사용하기 위해 인터페이스를 변경해준다. (함수 이름 변경)
class Text : public TextView, public Shape
{
public:
Text(string s) : TextView(s) { }
virtual void Draw() { TextView::Show(); }
};
그러면 main 함수에서 통일성을 유지하여 Draw를 계속 사용할 수 있다.
int main()
{
vector<Shape*> v;
v.push_back(new Rect);
v.push_back(new Circle);
v.push_back(new Text("hello"));
for (auto p : v)
{
p->Draw();
}
}
이렇게 호환되지 않는 인터페이스를 호환 되도록 해주는 것이 Adapter 이다.
그렇다면 위의 main()에서 다음과 같이 TextView 객체를 직접 사용하는 것은 어떨까?
TextView tv("world"); // 이미 존재하던 객체를 PUSH
v.push_back(&tv); // Error
이것은 에러이다. Text는 TextView의 인터페이스를 수정해서 Shape*와 일단 호환은 되었지만 TextView 객체 tv는 tv자체의 인터페이스 수정이 필요하다. 그러나 이에 대한 Adapter가 없기에 Error가 된다.
Adapter는 다음과 같이 클래스 어댑터와 객체 어댑터로 나뉜다.
클래스 어댑터
클래스의 인터페이스를 변경한다.
다중 상속 또는 값으로 포함되는 경우가 많다.
이미 존재하던 개게의 인터페이스는 변경할 수 없다.
객체 어댑터
객체의 인터페이스를 변경한다.
구성(Composition, 포함)을 사용하는 경우가 많다.
기존 객체를 포인터 또는 참조로 포함시킨다.
그러므로 위에서 사용된 Text 클래스는 클래스 어댑터로 사용된 것이다. 위의 어댑터 클래스를 아래와 같이 쓸 수도 있겠지만 이 경우 TextView의 멤버함수를 사용할 수 없으므로 위의 표현이 더 좋다고 할 수 있다.
class Text : public Shape
{
TextView tv;
public:
Text(string s) : tv(s) { }
virtual void Draw() { tv.Show(); }
};
그렇다면 이에 대한 객체 어댑터를 만들어 보자. TextView의 포인터가 가 클래스 내에 포함 되어있다.
class ObjectAdapter : public Shape
{
TextView* pView; // 포인터가 핵심
public:
ObjectAdapter(TextView* p) : pView(p) { }
virtual void Draw() { pView->Show(); }
};
'프로그래밍 이야기 > C++ 기초' 카테고리의 다른 글
[DesignPattern] Facade (0) | 2021.01.19 |
---|---|
[C++]Design Pattern - STL과 Adapter (0) | 2020.12.30 |
[게임이론] 벡터-내적의 활용 (0) | 2020.12.26 |
[C++]Design Pattern - Decorator Pattern (0) | 2020.12.18 |
[C++] 일반 함수에서의 가변성 (0) | 2020.12.11 |