비지터

Hunter Joe·2026년 4월 10일

정의

방문자 패턴은 알고리즘들을 그들이 작동하는 객체들로부터 분리할 수 있도록 하는 행동 디자인 패턴입니다.

알고리즘들을 그들이 작동하는 객체들로부터 분리한다라..

문제

  1. 기존 클래스 변경 안돼
  2. XML 내보기내기 메서드가 해당 클래스에 있는게 맞아? <-SRP
  3. 다른 형식으로 내보내기 기능이 추가될꺼 같은데? <- OCP

해결

새로운 메서드를 기존 클래스에 통합하는 대신에 visitor라는 별도의 클래스에 배치하자

class ExportVisitor implements Visitor is
    method doForCity(City c) { ... }
    method doForIndustry(Industry f) { ... }
    method doForSightSeeing(SightSeeing ss) { ... }
    // …

해당 메서드를 정확히 어떻게 호출할 수 있을까? 특히 전체 그래프(foreach)를 다룰 때,
이 메서드들은 시그니처들이 다르므로 다형성을 사용할 수 없습니다.

for Each {
    if (node instance of City) 
        exportVistor.doForCity((City) node)
    if (node instance of Industry)
        exportVisitor.doForIndustry((Industry) node)
}

이 방법은 번거로운 조건문 없이 객체에 적절한 메서드를 실행하는 것을 목표로합니다.
객체들은 자신의 클래스들을 알고 있으므로 비지터가 올바른 메서드를 선택할 수 있도록 할 수 있습니다.

foreach (Node node in graph)
    node.accept(exportVisitor)

// City
class City is
    method accept(Visitor v) is
        v.doForCity(this)
    // …

// Industry
class Industry is
    method accept(Visitor v) is
        v.doForIndustry(this)
    // …

노드(Element) 안바꾼다면서 바꿨.... accept 추가했네..

다이어그램

의사코드

// interface - element
interface Shape is 
	method move(x,y)
    method draw()
    method accept(v: Vistor)

// implement - concrete element
class Dot implements Shape is
	method accept(v: Visitor) is
    	v.visitDot(this)
        
class Circle implements Shape is
    // …
    method accept(v: Visitor) is
        v.visitCircle(this)

class Rectangle implements Shape is
    // …
    method accept(v: Visitor) is
        v.visitRectangle(this)

class CompoundShape implements Shape is
    // …
    method accept(v: Visitor) is
        v.visitCompoundShape(this)

// interface - visitor
interface Visitor is 
    method visitDot(d: Dot)
    method visitCircle(c: Circle)
    method visitRectangle(r: Rectangle)
    method visitCompoundShape(cs: CompoundShape)

// implements - concrete visitor 
class XMLExportVisitor implements Visitor is
    method visitDot(d: Dot) is
        // 점의 아이디와 중심 좌표를 내보냅니다.

    method visitCircle(c: Circle) is
        // 원의 아이디, 중심 좌표 및 반지름을 내보냅니다.

    method visitRectangle(r: Rectangle) is
        // 사각형의 아이디, 왼쪽 상단 좌표, 너비 및 높이를 내보냅니다.

    method visitCompoundShape(cs: CompoundShape) is
        // 모양의 아이디와 그 자식들의 아이디 리스트를 내보냅니다.
        
// 클라이언트 코드 
class Application is
    field allShapes: array of Shapes[]

    method export() is
        exportVisitor = new XMLExportVisitor()

        foreach (shape in allShapes) do
            shape.accept(exportVisitor)
            

더블 디스패치

accept 이 필요한 이유 더블 디스패치 아래서 한번 더 보자

foreach (shape in allShapes) do
    shape.accept(exportVisitor)

allShapes에 Dot, Circle 등이 섞여 있지만 루프에서는 전부 Shape 타입으로 잡힘
컴파일러는 구체적으로 뭔지 모름

1단계 디스패치 : Shape 타입 결정하기

shape.accept(exportVisitor)를 호출하면 런타임에 shape의 실제 타입에 따라 해당 클래스의 accept가 호출,
예를 들어 실제로 Dot 객체라면

class Dot implements Shape is
    method accept(v: Visitor) is
        v.visitDot(this)  

이 시점에서 Dot이 확정 이게 첫 번째 디스패치

2단계 디스패치 :Visitor 타입 결정

accept 안에서 v.visitDot(this)를 호출하면, v의 실제 타입에 따라 어떤 visitDot이 실행될지 결정
vXMLExportVisitor라면

class XMLExportVisitor implements Visitor is
    method visitDot(d: Dot) is
        // 점의 아이디와 중심 좌표를 내보냅니다.  

한번의 호출로는 shape, visitor 두 축을 동시에 결정할 수 없어서 두번의 호출로 각각 다형성을 적용
그래서 더블 디스패치..

그러니깐 accept 쪽에서 자기 자신을 this로 넘겨줘야
visitor에서 해당 구현된 shape의 데이터에 접근된다는건데 ..

적용은

profile
Improvise, Adapt, Overcome

0개의 댓글