프로그래밍 기술/Escort GoF의 디자인 패턴

26. 방문자 패턴(Visitor Pattern) [Escort GoF의 디자인 패턴]

언제나휴일 2016. 4. 4. 15:23
반응형

26. 방문자 패턴(Visitor Pattern)

 

26.1 개요

 

 프로그래밍하다 보면 여러 구성 요소로 구성된 개체를 정의해야 하는 경우는 매우 흔한 일입니다. 그리고 특정 명령을 수행함에 있어 내부 구성 요소 개체들의 형식에 따라 적용해야 할 구체적 행위가 다른 경우가 있습니다. 이 경우에 방문자 패턴을 사용하면 개체들의 형식에 따라 적용해야 할 구체적 행위를 분리하여 정의할 수 있습니다. 이는 새로운 행위를 정의할 필요가 생길 경우에도 구성 요소 형식을 변경하지 않으면서 추가할 수 있게 됩니다.

 

 방문자 패턴에서는 개체를 구성하는 여러 요소 개체들에게 요소의 형식에 따라 수행할 구체적 작업을 방문자에 정의하게 됩니다. 대신 요소의 형식에서는 방문자를 수용하는 메서드를 제공하고 방문자를 통해 자신을 제어하기 위한 약속된 메서드를 사용하면 방문자가 구체적인 작업을 대행하게 됩니다. 이처럼 방문자 패턴을 사용하면 요소 형식에 따라 수행할 구체적 작업을 분리하여 구현할 수 있다는 장점과 새로운 연산을 요소 형식 변경 없이 가능하다는 장점이 있습니다. 하지만, 정보 은닉이 필요한 부분까지 방문자에게 노출해야 하기 때문에 신뢰성을 떨어지는 원인이 되기도 합니다.  또한, 요소의 형식이 추가되면 방문자 형식에 추가된 요소 형식의 개체에 대한 연산을 추가해야 하는 숨어있는 비용이 발생합니다. 만약, 요소 형식이 추가할 확률이 높다면 방문자 패턴은 예상치 못했던 비용이 발생할 수 있습니다.

 

 참고 사항으로 방문자를 수용하는 메소드에서는 방문자의 형식과 요소의 형식에 따라 수행되는 연산이 달라지게 되므로 이중 디스패치 기법을 표현해야 하는데 언어에서 제공된다면 굳이 방문자 패턴을 사용할 필요가 없습니다. C++언어에서는 이중 디스패치를 지원하지 않습니다.

 

 

 

26. 2 시나리오

 

 매 주 여행을 다닌지도 6개월이 지났습니다. 그리고 여행을 다니면서 추억을 담은 사진과 여행에 다녀온 후기들에 대한 통합 관리가 필요함을 점점 느끼고 있습니다. 그리하여 이번에 사진들과 일기 등을 통합 관리하는 프로그램을 만들려고 합니다.

 

 기본적으로 사진과 일기를 같은 방법으로 관리하려 합니다. 전체 사진과 일기를 보거나 필요한 경우 모든 내용을 초기화하는 등의 기능을 점진적으로 추가할거예요. 물론, 사진과 일기에 대한 정보를 보여주는 방식은 서로 다르기 때문에 이에 대한 구체적인 구현은 다르겠지요. 그 외에 다른 기능들도 사진이나 일기에 따라 다르게 구현해야 할 것들은 많을 것이라 생각해요.

 

 새로운 기능을 추가할 때마다 사진과 일기에 각 기능을 구현하는 것은 많은 비용이 들 것 같아요. 그래서 새로운 기능이 추가될 때마다 사진과 일기에 적용할 수 있는 새로운 형식을 추가하는 형태로 설계하려고 고민 중에 있어요.


방문자 패턴



AboutVisitor.zip

//Common.h

#pragma once

 

#pragma warning (disable:4996)

#include <iostream>

 

using std::cout;

using std::endl;

using std::cin;

#include <vector>

using std::vector;

#include <string>

using std::string;

 

#include <iomanip>

using std::ios;

#include <algorithm>

 

#pragma warning(disable:4482)

 

//Element.h

#pragma once

#include "Common.h"

 

#include "Visitor.h"

class Element

{

             string name;         

public:

             Element(string name)

             {

                           this->name = name;

             }

             string GetName()const

             {

                           return name;

             }

             virtual void Accept(Visitor *visitor)=0;

};

 

//Visitor.h

#pragma once

#include "Common.h"

 

class Picture;

class Diary;

class Visitor

{           

public:

             virtual void VisitPicture(Picture *picture)=0;

             virtual void VisitDiary(Diary *diary)=0;

};

 

//Diary.h

#pragma once

#include "Element.h"

class Diary:

             public Element

{           

             string content;

public:

             Diary(string name,string content):Element(name)

             {

                           SetContent(content);

             }

             void ViewContent()

             {

                           cout<<content<<endl;

             }

             void SetContent(string content)

             {

                           this->content = content;

             }

             virtual void Accept(Visitor *visitor)

             {                         

                           visitor->VisitDiary(this);

             }

};

 

//Picture.h

#pragma once

#include "Element.h"

 

class Picture:

             public Element

{           

             int tone;

             int brightness;

             int saturation;

public:

             Picture(string name,int tone,  int brightness,int saturation):Element(name)

             {

                           SetInfo(tone,brightness,saturation);

             }

             void ViewInfo()

             {

                           cout<<"색조:"<<tone<<" 명도:"<<brightness<<" 채도:"<<saturation<<endl;

             }

             void SetInfo(int tone,           int brightness,int saturation)

             {

                           this->tone = tone;

                           this->brightness = brightness;

                           this->saturation = saturation;

             }

             virtual void Accept(Visitor *visitor)

             {                         

                           visitor->VisitPicture(this);

             }

};

 

//Initializer.h

#pragma once

#include "Visitor.h"

 

#include "Picture.h"

#include "Diary.h"

class Initializer:

             public Visitor

{           

public:

             virtual void VisitPicture(Picture *picture)

             {

                           picture->SetInfo(0,0,0);

             }

             virtual void VisitDiary(Diary *diary)

             {

                           diary->SetContent("내용없음");

             }

};

 

//Viewer.h

#pragma once

#include "Visitor.h"

 

#include "Picture.h"

#include "Diary.h"

class Viewer:

             public Visitor

{           

public:

             virtual void VisitPicture(Picture *picture)

             {

                           cout<<"사진 제목:"<<picture->GetName()<<endl;

                           picture->ViewInfo();

             }

             virtual void VisitDiary(Diary *diary)

             {

                           cout<<diary->GetName()<<endl;

                           diary->ViewContent();

             }

};

 

//MyTour.h

#pragma once

#include "Initializer.h"

#include "Viewer.h"

#include "Picture.h"

#include "Diary.h"

 

typedef vector<Element *> Collection;

typedef vector<Element *>::iterator CIter;

class MyTour

{

             Collection collection;

             Visitor *visitors[2];

public:

             MyTour()

             {

                           visitors[0] = new Viewer();

                           visitors[1] = new Initializer();

             }

             void AddElement(Element *element)

             {

                           collection.push_back(element);

             }

             void ViewAll()

             {

                           CIter seek = collection.begin();

                           CIter end = collection.end();

 

                           Element *element =0;

                           for( ; seek != end ; ++seek)

                           {

                                        element = *seek;

                                        element->Accept(visitors[0]);

                           }

             }

             void Reset()

             {

                           CIter seek = collection.begin();

                           CIter end = collection.end();

 

                           Element *element =0;

                           for( ; seek != end ; ++seek)

                           {

                                        element = *seek;

                                        element->Accept(visitors[1]);

                           }

                           cout<<"모든 정보를 초기 상태로 바꾸었습니다."<<endl;

             }

};

 

//Demo.cpp

#include "MyTour.h"

int main()

{

             MyTour *mt = new MyTour();

 

             Picture *e = new Picture("A",1,1,1);

             Diary *d = new Diary("2016.2.26","언제나 휴일");

 

             mt->AddElement(e);

             mt->AddElement(d);

 

             mt->ViewAll();

             mt->Reset();

             mt->ViewAll();

 

             delete e;

             delete d;

             delete mt;

             return 0;

}


IT 전문가로 가는 길 Escort GoF의 디자인 패턴
국내도서
저자 : 장문석
출판 : 언제나휴일 2013.04.01
상세보기



반응형