#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> s = {1,2,3,4,5,6,7,8,9,10};
for(auto& n : s)
n = n * 2; // 매번 이런식으로 직접 접근해야하나?
// 멤버 함수로 기능을 제공해보자.
s.twict_all_element();
// 그런데? 멤버함수를 매번 추가하는게 보통일이 아닐텐데?
// -> 방문자 패턴을 써보자.
TwictVisitor<int> tv;
a.accept(&tv);
}
- Visitor
- 객체 구조에 속한 요소에 수행할 오퍼레이션을 정의하는 객체
- 클래스를 변경하지 않고 새로운 오퍼레이션을 정의할 수 있다.
template<typename T> struct IVisitor
{
virtual void visit(T& elem) = 0;
virtual ~IVisitor() {}
};
template<typename T> class TwiceVisitor : public IVisitor<T>
{
public:
virtual void visit(T& elem) { elem = elem * 2; }
};
// 방문 대상의 인터페이스
struct IAcceptor
{
virtual void accept(IVisitor<T>* p) = 0;
virtual ~IAcceptor() {}
};
template<typename T> class List : public list<T>, public IAccpetor<T>
{
public:
using list<T>::list; // 생성자 상속
virtual void accept(IVisitor<T>* p)
{
// 모든 요소를 방문자에게 전달.
for(auto& e : *this)
p->visit(e);
}
};
방문자 패턴을 사용하는 또 다른 코드
int main()
{
PopupMenu* p1 = new PopupMenu("MENUBAR");
p1->addMenu(new PopupMenu("SCREEN"));
p1->addMenu(new PopupMenu("SOUND"));
p1->addMenu(new MenuItem("power off"), 11);
// PopupMenu는 []를 붙여서 이름을 붙이고 싶다
// 단, PopupMenu의 내부를 수정하지 않고!
MenuTitleChangeVisitor mtcv;
p1->accept(&mtcv);
p1->command();
}
class MenuItem : public BaseMenu
{
int id;
public:
virtual void accept(IMenuVisitor* p)
{
p->visit(this);
}
// ...
}
class PopupMenu : public BaseMenu
{
// ...
virtual void accept(IMenuVisitor* p)
{
p->visit(this);
for(auto m : v)
m->accept(p);
}
// ...
}
class MenuTitleChangeVisitor : public IMenuVisitor
{
public:
virtual void visit(BaseMenu* p) {}
virtual void visit(MenuItem* p) {}
virtual void visit(PopupMenu* p) {
// popupmenu의 타이틀을 변경한다.
string s = p->getTitle();
s = "[" + s + "]";
p->setTitle(s);
}
}
- Visitor 패턴의 특징
- 일반 적인 객체 지향 프로그래밍 언어에서는, 새로운 타입을 추가하기는 쉽지만 새로운 함수를 추가하기는 어렵다.(베이스 클래스부터 시작해서 상속을 받는 모든 클래스를 다 수정해야하기 때문에)
- 방문 패턴은 새로운 오퍼레이션을 추가하기는 쉽지만 타입을 추가하기는 어렵다(타입추가시 인터페이스에서 추가된 타입을 새로 넣어주어야한다.)