안녕하세요! GoF 디자인 패턴 연재, 이번 시간에는 행동 패턴(Behavioral Patterns)의 마지막 주자이며 이번 GoF 포스팅의 마지막 대미를 장식하게될 비지터(Visitor) 패턴에 대해 알아보겠습니다. 이 패턴은 객체의 구조는 변경하지 않으면서, 그 구조의 요소들에 대한 새로운 연산(Operation)을 추가할 수 있게 해줍니다.
정의
비지터(Visitor) 패턴은 데이터 구조(객체 구조)와 해당 구조에 대한 처리(연산)를 분리하는 패턴입니다. 즉, 실제 연산을 수행하는 '방문자(Visitor)' 객체가 데이터 구조를 돌아다니며 각 요소(Element)에 대한 작업을 수행하는 방식입니다.
동물원에 여러 동물(사자, 코끼리, 원숭이)이 있다고 상상해봅시다.
- 수의사(Visitor 1)는 각 동물을 방문하며 건강 상태를 체크합니다.
- 먹이 담당자(Visitor 2)는 각 동물을 방문하며 적절한 먹이를 줍니다.
여기서 중요한 점은, 동물(Element) 자체는 '건강 체크'나 '먹이 주기' 로직을 가질 필요가 없다는 것입니다. 새로운 작업, 예를 들어 '인기 투표하기(Visitor 3)'가 필요해지면, 새로운 방문자만 추가하면 될 뿐 기존 동물 클래스들은 전혀 수정할 필요가 없습니다. 이처럼 데이터 구조는 그대로 둔 채, 새로운 기능(연산)을 '방문자(Visitor)' 형태로 손쉽게 추가할 수 있게 하는 것이 비지터 패턴의 핵심입니다.
Visitor 패턴의 구현에는 다음과 같은 Class가 사용됩니다.
- Visitor (방문자): 데이터 구조의 각 ConcreteElement에 대해 visit() 메서드를 선언합니다. 즉, visit(ConcreteElementA), visit(ConcreteElementB) 처럼 각 요소 클래스별로 오버로딩된 메서드를 가집니다.
- ConcreteVisitor (구체적인 방문자): Visitor 인터페이스를 구현하며, 각 visit() 메서드에 실제 처리 로직을 담습니다.
- Element (요소): 방문자를 받아들이는 accept(Visitor v) 메서드를 정의하는 인터페이스입니다.
- ConcreteElement (구체적인 요소): Element 인터페이스를 구현합니다. accept(Visitor v) 메서드 내부에서는 보통 visitor.visit(this)를 호출하여, 방문자가 자신에 맞는 visit 메서드를 실행하도록 합니다.
- ObjectStructure (객체 구조): Element들의 집합을 관리하며, 클라이언트가 방문자를 통해 모든 요소를 순회할 수 있도록 하는 인터페이스를 제공합니다.
사용예시
컴퓨터의 각 부품(키보드, 모니터 등)에 대해, 부품 정보 표시라는 연산을 비지터 패턴으로 구현해 보겠습니다.
1. 요소 (Element) 및 방문자 (Visitor) 인터페이스 정의
먼저 컴퓨터 부품(ComputerPart)과 방문자(ComputerPartVisitor)의 공통 인터페이스를 정의합니다.
// Visitor 인터페이스
interface ComputerPartVisitor {
void visit(Keyboard keyboard);
void visit(Monitor monitor);
void visit(Mouse mouse);
void visit(Computer computer);
}
// Element 인터페이스
interface ComputerPart {
void accept(ComputerPartVisitor computerPartVisitor);
}
2. 구체적인 요소 (ConcreteElement) 클래스 구현
각 컴퓨터 부품 클래스를 구현합니다. accept 메서드가 핵심입니다.
// ConcreteElement A
class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
// ConcreteElement B
class Monitor implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
// ConcreteElement C
class Mouse implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
3. 객체 구조 (ObjectStructure) 및 구체적인 방문자 (ConcreteVisitor) 구현
컴퓨터 부품들을 모아놓은 Computer 클래스와, 부품 정보를 표시하는 ComputerPartDisplayVisitor를 구현합니다.
// ObjectStructure (Composite 역할도 겸함)
class Computer implements ComputerPart {
ComputerPart[] parts;
public Computer(){
parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
}
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
// ConcreteVisitor 클래스
class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("컴퓨터 부품들을 표시합니다.");
}
@Override
public void visit(Mouse mouse) {
System.out.println("마우스를 표시합니다.");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("키보드를 표시합니다.");
}
@Override
public void visit(Monitor monitor) {
System.out.println("모니터를 표시합니다.");
}
}
4. 클라이언트 (Client) 코드
클라이언트는 객체 구조에 방문자를 전달하여 연산을 수행합니다.
public class Demo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
// 실행 결과:
// 마우스를 표시합니다.
// 키보드를 표시합니다.
// 모니터를 표시합니다.
// 컴퓨터 부품들을 표시합니다.
}
}
만약 '부품 가격 계산'이라는 새로운 기능이 필요하다면, ComputerPartPricingVisitor라는 새로운 방문자만 추가하면 됩니다. 기존 ComputerPart 클래스들은 전혀 수정할 필요가 없습니다.
결론
Visitor Class는 집합된 객체에 대해 일괄 연산을 여러변, 다양하게 수행하고 싶은 때 사용하며 다음과 같은 경우에도 사용됩니다.
- 다양한 클래스의 객체들로 이루어진 복잡한 객체 구조에 대해 연산을 수행해야 할 때
- 관련된 연산들을 하나의 방문자 클래스에 모아 관리하고 싶을 때
다만 다음과 같은 조건을 고려해야합니다.
- 새로운 Element 추가의 어려움: 새로운 ConcreteElement 클래스가 추가되면, 모든 Visitor 인터페이스에 visit(NewElement) 메서드를 추가해야 합니다.
- 캡슐화 위반: 방문자는 요소의 내부 상태에 접근해야 하는 경우가 많아, 요소 클래스가 자신의 내부 상태를 노출하는 public 메서드를 제공해야 할 수 있습니다.
비지터 패턴은 데이터 구조와 알고리즘을 분리하는 강력한 메커니즘을 제공합니다. 데이터 구조가 안정적인 반면, 수행해야 할 작업이 자주 변경되거나 확장될 가능성이 높은 시스템에서 그 진가를 발휘합니다.
자 지금까지 Gang Of Four의 모든 패턴을 알아보았습니다. 다음 포스팅에서는 기술사를 공부하고 있는 사람으로서 각 패턴에 대해서 핵심적인 내용을 요약하는 시간과 패턴포스트에 올라간 사진들 및 키워드를 확인하는 시간을 가져보겠습니다 다들 긴 시간 수고많으셨습니다~!😀😀
'정보보안-이론 > XX에 대하여' 카테고리의 다른 글
[디자인 패턴] (GoF 행위패턴) Template Metho Pattern에 대하여 (0) | 2025.07.14 |
---|---|
[디자인 패턴] (GoF 행위패턴) Observer Pattern에 대하여 (0) | 2025.07.13 |
[디자인 패턴] (GoF 행위패턴) Strategy Pattern에 대하여 (0) | 2025.07.13 |
[디자인 패턴] (GoF 행위패턴) State Pattern에 대하여 (0) | 2025.07.12 |
[디자인 패턴] (GoF 행위패턴) Interpreter Pattern에 대하여 (0) | 2025.07.12 |