[Design Pattern] 방문자 패턴

Visitor Pattern

방문자 패턴은 객체 구조를 이루는 원소에 대해 수행할 연산을 표현한다. 연산을 적용할 원소의 클래스를 변경하지 않고도 새로운 연산을 정의할 수 있게 한다.

활용성

  • 객체 구조에 포함된 원소에 대해 구체 방문자 클래스에 따라 달라진 연산을 원소에 대해 수행할 때
  • 각각 특징이 있고 관련되지 않은 많은 연산이 한 객체 구조에 속해있는 객체들에 대해 수행될 필요가 있으며 연산으로 클래스들을 더럽히고 싶지 않을 때
  • 객체 구조를 정의한 클래스는 거의 변하지 않지만 전체 구조에 걸쳐 새로운 연산을 추가하고 싶을 때

방문자 패턴 사용에 따른 결과

쉽게 연산 추가 가능

방문자 클래스는 복잡한 객체를 구성하는 요소에 대하여 연산을 지원하기 때문에 새로운 연산을 쉽게 추가할 수 있다.

관련된 연산의 응집도 상승

관련된 행동들이 객체 구조를 정의하는 클래스에 분산되지 않고 방문자 클래스에 모이게 된다. 이렇게 수행함으로써 관련된 연산끼리의 응집도가 상승하고 관련되지 않은 연산은 배제시킬 수 있다.

새로운 구체 원소 클래스 추가의 어려움

새로운 구체 원소 클래스가 생길수록 모든 방문자 클래스에 새로운 원소에 대한 지원이 이루어져야 하기 때문에 어려움이 생긴다.

클래스 계층 구조와 관계 없음

가령 반복자 패턴을 예로 들면 반복자 패턴은 순회를 하는데 해당 객체와 관련된 계층 구조를 가진 클래스끼리만의 순회가 가능하다. 하지만 방분자 패턴은 이와는 별개로 원소들끼리의 계층 구조가 이루어지지 않아도 방문이 가능하다.

상태의 누적 가능

방문자 패턴은 객체 구조 내 각 원소들을 순회하며 상태를 누적시킬 수 있다.

캡슐화 위배

방문자 패턴은 원소가 방문자에게 필요한 작업을 위임시킬만큼 강력한 영향력을 행사할 수 있다. 이는 곧, 원소 내부 상태 접근에 있어 필요한 연산들을 공개 API로 만들 수 밖에 없게 되는데 이는 캡슐화를 위배한다.

방문자

구조

방문자의 구조는 다음과 같다.

Visitor Pattern Diagram

C++ 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void VisitorPattern::VisitorPattern()
{
buildIronMan();
buildGundam();
}

void VisitorPattern::buildIronMan()
{
RobotElementSuit* ironMan = new RobotElementSuit();
ironMan->activate(new IronManVisitor());
}

void VisitorPattern::buildGundam()
{
RobotElementSuit* gundam = new RobotElementSuit();
gundam->activate(new GundamVisitor());
}
1
2
3
4
5
6
class RobotElement
{
public:
virtual ~RobotElement() {}
virtual void activate(RobotVisitor* visitor) = 0;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class RobotElementArm : public RobotElement
{
std::string position;
public:
RobotElementArm(std::string position);
void activate(RobotVisitor* visitor) override;
};

RobotElementArm::RobotElementArm(std::string position) : position(position) {}

void RobotElementArm::activate(RobotVisitor* visitor)
{
visitor->activate(this);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class RobotElementLeg : public RobotElement
{
std::string position;
public:
RobotElementLeg(std::string position);
void activate(RobotVisitor* visitor) override;
};

RobotElementLeg::RobotElementLeg(std::string position) : position(position) {}

void RobotElementLeg::activate(RobotVisitor* visitor)
{
visitor->activate(this);
}
1
2
3
4
5
6
7
8
9
10
class RobotElementBody : public RobotElement
{
public:
void activate(RobotVisitor* visitor) override;
};

void RobotElementBody::activate(RobotVisitor* visitor)
{
visitor->activate(this);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class RobotElementSuit : public RobotElement
{
std::list<RobotElement*> robotElements;
public:
RobotElementSuit ();

void activate(RobotVisitor* visitor) override;
};

RobotElementSuit::RobotElementSuit ()
{
robotElements.push_back(new RobotElementArm("Right"));
robotElements.push_back(new RobotElementArm("Left"));
robotElements.push_back(new RobotElementLeg("Right"));
robotElements.push_back(new RobotElementLeg("Left"));
robotElements.push_back(new RobotElementBody());
}

void RobotElementSuit::activate(RobotVisitor* visitor)
{
for(RobotElement* element : robotElements)
element->activate(visitor);
visitor->activate(this);
}
1
2
3
4
5
6
7
8
9
class RobotVisitor
{
public:
virtual ~RobotVisitor() {}
virtual void activate(RobotElementArm* arm) = 0;
virtual void activate(RobotElementLeg* leg) = 0;
virtual void activate(RobotElementBody* body) = 0;
virtual void activate(RobotElementSuit* suit) = 0;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class IronManVisitor: public RobotVisitor
{
public:
void activate(RobotElementArm* arm) override;
void activate(RobotElementLeg* leg) override;
void activate(RobotElementBody* body) override;
void activate(RobotElementSuit* suit) override;
};

void IronManVisitor::activate(RobotElementArm* arm)
{
std::cout<<"Iron Man Arm on Activation"<<std::endl;
}

void IronManVisitor::activate(RobotElementLeg* leg)
{
std::cout<<"Iron Man Leg on Activation"<<std::endl;
}

void IronManVisitor::activate(RobotElementBody* body)
{
std::cout<<"Iron Man Body on Activation"<<std::endl;
}

void IronManVisitor::activate(RobotElementSuit* suit)
{
std::cout<<"Iron Man Suit on Active"<<std::endl;
std::cout<<"Let's Roll!!"<<std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class GundamVisitor : public RobotVisitor
{
public:
void activate(RobotElementArm* arm) override;
void activate(RobotElementLeg* leg) override;
void activate(RobotElementBody* body) override;
void activate(RobotElementSuit* suit) override;

};

void GundamVisitor::activate(RobotElementArm* arm)
{
std::cout<<"Gundam Arm on Activation"<<std::endl;
}

void GundamVisitor::activate(RobotElementLeg* leg)
{
std::cout<<"Gundam Leg on Activation"<<std::endl;
}

void GundamVisitor::activate(RobotElementBody* body)
{
std::cout<<"Gundam Body on Activation"<<std::endl;
}

void GundamVisitor::activate(RobotElementSuit* suit)
{
std::cout<<"Gundam Robot on Activation"<<std::endl;
std::cout<<"アムロ行きます!!"<<std::endl;
}

java 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Client {
public static void main(String[] args) {
buildIronMan();
buildGundam();
}

public static void buildIronMan() {
RobotElementSuit ironMan = new RobotElementSuit();
ironMan.activate(new IronManVisitor());
}

public static void buildGundam() {
RobotElementSuit gundam = new RobotElementSuit();
gundam.activate(new GundamVisitor());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public interface RobotElement {
void activate(RobotVisitor visitor);
}

public class RobotElementArm implements RobotElement{
@Override
public void activate(RobotVisitor visitor) {
visitor.activate(this);
}
}

public class RobotElementLeg implements RobotElement {
@Override
public void activate(RobotVisitor visitor) {
visitor.activate(this);
}
}

public class RobotElementBody implements RobotElement {
@Override
public void activate(RobotVisitor visitor) {
visitor.activate(this);
}
}

public class RobotElementSuit implements RobotElement {
private List<RobotElement> list;

public RobotElementSuit() {
list = new LinkedList<>();
list.addAll(Arrays.asList(
new RobotElementArm(),
new RobotElementArm(),
new RobotElementLeg(),
new RobotElementLeg(),
new RobotElementBody()
));
}
@Override
public void activate(RobotVisitor visitor) {
for(RobotElement element : list)
element.activate(visitor);
visitor.activate(this);
}
}
1
2
3
4
5
6
public interface RobotVisitor {
public void activate(RobotElementArm arm);
public void activate(RobotElementLeg leg);
public void activate(RobotElementBody body);
public void activate(RobotElementSuit suit);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class IronManVisitor implements RobotVisitor {
@Override
public void activate(RobotElementArm arm) {
System.out.println("Iron Man Arm on Activation");
}

@Override
public void activate(RobotElementLeg leg) {
System.out.println("Iron Man Leg on Activation");
}

@Override
public void activate(RobotElementBody body) {
System.out.println("Iron Man Body on Activation");
}

@Override
public void activate(RobotElementSuit suit) {
System.out.println("Iron Man Suit on Active");
System.out.println("Let's Roll!!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class GundamVisitor implements RobotVisitor {
@Override
public void activate(RobotElementArm arm) {
System.out.println("Gundam Arm on Activation");
}

@Override
public void activate(RobotElementLeg leg) {
System.out.println("Gundam Leg on Activation");
}

@Override
public void activate(RobotElementBody body) {
System.out.println("Gundam Body on Activation");
}

@Override
public void activate(RobotElementSuit suit) {
System.out.println("Gundam Robot on Active");
System.out.println("アムロ行きます!!");
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/31/Design%20Pattern/VisitorPattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.