(C++ : Design-pattern) Visitor Pattern

Posted by : at

Category : Cpp   Design-pattern


#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 패턴의 특징
    • 일반 적인 객체 지향 프로그래밍 언어에서는, 새로운 타입을 추가하기는 쉽지만 새로운 함수를 추가하기는 어렵다.(베이스 클래스부터 시작해서 상속을 받는 모든 클래스를 다 수정해야하기 때문에)
    • 방문 패턴은 새로운 오퍼레이션을 추가하기는 쉽지만 타입을 추가하기는 어렵다(타입추가시 인터페이스에서 추가된 타입을 새로 넣어주어야한다.)

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links