// 방문자( Visitor )를 적용한 버전.
// Accepter가 있어야 한다.
#include <iostream>
#include <conio.h>
#include <string>
#include <vector>
using namespace std;
#define clrscr() system("cls")
// Acceptor 인터페이스- 모든 방문자를 받아 들일수 있어야 한다.
class IVisitor;
struct IAcceptor
{
virtual void accept( IVisitor* p ) = 0;
};
// Visitor 인터페이스- 방문 할수 있어야 한다.
struct IVisitor
{
virtual void visit( IAcceptor* p ) = 0;
};
class AbstractMenu : public IAcceptor
{
string title;
public:
AbstractMenu( string s ) : title(s) { }
virtual string GetTitle() const { return title; }
virtual void SetTitle( string s ) { title = s; }
virtual void Command() = 0; // 순수가상함수- 자식은 꼭 만들어야 한다.
virtual void accept( IVisitor* p )
{
p->visit( this ); // Visitor 설계의 핵심
}
};
class PopupMenu : public AbstractMenu
{
vector<AbstractMenu*> menu_list; // Composite
public:
PopupMenu(string s) : AbstractMenu(s) { }
void Add( AbstractMenu* p ) { menu_list.push_back( p ); }
void Remove() { }
virtual string GetTitle() const
{
return string("[") + AbstractMenu::GetTitle() + string("]");
}
// 팝업 메뉴가 선택되었을때를 처리한다.
virtual void Command()
{
while(1)
{
// 화면을지우고자신의하위메뉴를열거해준다.
clrscr();
for( int i = 0; i < menu_list.size(); ++i )
{
cout << i+1 <<"."<< menu_list[i]->GetTitle() <<endl;
}
cout << menu_list.size() + 1 << ". 이전메뉴로" << endl;
//------------------------------------------------
int cmd;
cout << "메뉴를생성하세요>> ";
cin >> cmd;
if ( cmd == menu_list.size()+1 ) // 이전메뉴로 선택
break; // 루프 탈출해서 자신을 이 함수를 마치고
// 자신을 호출한 부분으로 돌아간다.
// 잘못 선택한 경우
if ( cmd <= 0 || cmd > menu_list.size() )
continue;
// 해당 메뉴를 실행한다.
menu_list[cmd-1]->Command();
}
}
// 팝업 메뉴의 경우 모든 항목에 대해 visit를 호출해야 한다.
virtual void accept( IVisitor* p )
{
p->visit( this ); // 먼저 자신을 보내고
for( int i = 0; i < menu_list.size(); ++i )
menu_list[i]->accept( p );
}
};
// 메뉴를 처리하고 싶은 모든 클래스는 아래 인터페이스를 구현해야 한다.
struct IMenuHandler
{
virtual void MenuCommand( int id ) = 0;
};
class MenuItem : public AbstractMenu
{
int id;
IMenuHandler* pMenuHandler;
public:
// 부모의 디폴트 생성자가 없으므로 여기서 초기화 해준다.
MenuItem( string s, int n, IMenuHandler* p )
: AbstractMenu(s), id(n), pMenuHandler(p) { }
// 메뉴 선택시 처리
virtual void Command()
{
if( pMenuHandler != 0 ) pMenuHandler->MenuCommand( id );
}
};
// 메뉴의 각 항목의 색상을 변경하는 visitor
class RedVisitor : public IVisitor
{
public:
virtual void visit( IAcceptor* p )
{
AbstractMenu* pMenu = (AbstractMenu*)p;
string s = pMenu->GetTitle();
s = s + string("-Red");
pMenu->SetTitle( s );
}
};
//------------------------------------------------------
class VideoShop : public IMenuHandler
{
public:
void Init()
{
PopupMenu* p1 = new PopupMenu("ROOT");
PopupMenu* p2 = new PopupMenu("고객관리");
PopupMenu* p3 = new PopupMenu("Tape관리");
PopupMenu* p4 = new PopupMenu("기타");
p1->Add( p2 );
p1->Add( p3 );
p1->Add( p4 );
p2->Add( new MenuItem("신규고객등록", 1, this) );
p2->Add( new MenuItem("고객삭제", 2, this) );
p2->Add( new MenuItem("기타", 3, this) );
p3->Add( new MenuItem("신규Tape등록", 4, this) );
p3->Add( new MenuItem("Tape삭제", 5, this) );
p3->Add( new MenuItem("기타", 6, this) );
RedVisitor r;
p1->accept(&r);
p1->Command();
}
void AddCustomer()
{
cout << "신규고객등록처리" << endl;
}
virtual void MenuCommand( int id )
{
// 메뉴 ID를 가지고 각 함수로 라우팅 한다.
if ( id == 1 ) AddCustomer();
cout << "다른ID처리" << endl;
getch();
}
};
int main()
{
VideoShop v;
v.Init();
}