방문자 Visitor 패턴 - 타입스크립트 예제
요약
방문자 visitor 패턴을 타입스크립트 코드와 함께 알아봅니다.
방문자 패턴 요약
패턴 종류 | 핵심 키워드 |
---|---|
방문자 (Visitor) | 객체의 내부 상태에 접근할 수 있는 방법 제공, 연산 추가, 기존 코드 변경하지 않고 새로운 기능 추가, 메서드가 각 클래스 돌아다님 |

방문자 (Visitor) 패턴
방문자 패턴은 데이터 구조(객체)와 해당 구조에서 수행할 처리(기능)를 분리하는 패턴입니다. 이 패턴을 사용하면, 데이터 구조를 변경하지 않으면서도 새로운 기능을 쉽게 추가할 수 있습니다.
'세무사' 를 비유로 들 수 있습니다. 개인의 자산에는 집, 자동차, 주식 등 여러 종류(데이터 구조)가 있습니다. 이때 '세금 계산'이라는 기능이 필요하다고 해서, 집 클래스에 calculateTax()
메서드를, 자동차 클래스에 calculateTax()
메서드를 각각 추가하는 것은 비효율적입니다. 자산의 종류가 늘어날 때마다 모든 클래스를 수정해야 하기 때문입니다.
대신, '세무사'라는 방문자(Visitor)를 만듭니다. 이 세무사는 각 자산(집, 자동차, 주식)을 '방문'하면서 세금을 계산하는 방법을 모두 알고 있습니다. 자산 객체들은 그저 세무사를 '받아들이기(accept
)'만 하면 됩니다. 나중에 '자산 감정 평가'라는 새로운 기능이 필요하면, '감정평가사'라는 새로운 방문자를 만들어 추가하기만 하면 됩니다. 기존의 자산 클래스들은 전혀 건드릴 필요가 없습니다.
기본 구조
- Visitor: 각
ConcreteElement
를 방문하는visit()
메서드를 선언합니다. (예:visitHouse(h)
,visitCar(c)
) - ConcreteVisitor:
Visitor
인터페이스를 구현하며, 각visit()
메서드에 실제 처리 로직을 담습니다. - Element: 방문자를 받아들이는
accept(visitor)
메서드를 정의합니다. - ConcreteElement:
Element
인터페이스를 구현하며,accept()
메서드 내부에서visitor.visit(this)
를 호출하여 방문자가 자신을 처리하도록 합니다. - ObjectStructure:
Element
들의 컬렉션이며, 방문자가 모든 요소를 순회하며 방문할 수 있도록 합니다.
예시: 동물원 동물의 행동 관찰하기
동물원의 동물(Element)들에게 '소리내기'와 '먹이주기'라는 행동(Visitor)을 적용해 보겠습니다.
// Visitor 인터페이스
interface AnimalVisitor {
visitLion(lion: Lion): void;
visitDolphin(dolphin: Dolphin): void;
}
// Element 인터페이스
interface Animal {
accept(visitor: AnimalVisitor): void;
}
// ConcreteElement 구현
class Lion implements Animal {
accept(visitor: AnimalVisitor): void {
visitor.visitLion(this);
}
roar(): void {
console.log("어흥!");
}
}
class Dolphin implements Animal {
accept(visitor: AnimalVisitor): void {
visitor.visitDolphin(this);
}
speak(): void {
console.log("끼이익!");
}
}
// ConcreteVisitor 구현
class SpeakVisitor implements AnimalVisitor {
visitLion(lion: Lion): void {
lion.roar();
}
visitDolphin(dolphin: Dolphin): void {
dolphin.speak();
}
}
class FeedVisitor implements AnimalVisitor {
visitLion(lion: Lion): void {
console.log("사자에게 고기를 줍니다.");
}
visitDolphin(dolphin: Dolphin): void {
console.log("돌고래에게 생선을 줍니다.");
}
}
// 클라이언트 코드 (ObjectStructure 역할)
const animals: Animal[] = [new Lion(), new Dolphin()];
const speakVisitor = new SpeakVisitor();
console.log("--- 소리내기 ---");
animals.forEach((animal) => animal.accept(speakVisitor));
// 출력:
// 어흥!
// 끼이익!
const feedVisitor = new FeedVisitor();
console.log("\n--- 먹이주기 ---");
animals.forEach((animal) => animal.accept(feedVisitor));
// 출력:
// 사자에게 고기를 줍니다.
// 돌고래에게 생선을 줍니다.
Lion
이나 Dolphin
클래스를 전혀 수정하지 않고도, SpeakVisitor
, FeedVisitor
라는 새로운 기능을 계속해서 추가할 수 있습니다. 이것이 방문자 패턴의 가장 큰 장점입니다.
방문자 패턴 중요 키워드
데이터 구조와 기능을 분리합니다.
개방-폐쇄 원칙(OCP): 기존 데이터 구조(Element)는 수정하지 않으면서, 새로운 기능(Visitor)을 쉽게 추가할 수 있습니다.
더블 디스패치(Double Dispatch):
accept()
와visit()
두 번의 호출을 통해, 실행 시점에 어떤Element
의 메서드를 호출할지, 그리고 어떤Visitor
의 메서드를 호출할지가 결정됩니다.데이터 구조가 자주 변경되면 모든 Visitor를 수정해야 하므로, 구조가 안정적일 때 사용하는 것이 좋습니다.
정처기 실기 기출 문제
기출 | |
문제 | 객체의 내부 상태에 접근할 수 있는 방법을 제공하여 호스트 객체에 연산을 추가할 수 있도록 하는 패턴을 보기에서 고르시오 |
보기 | |
답변 | |
정답 | 정답 보기 |