JAVA/JAVA Design Pattern

JAVA 비지터(Visitor) 패턴

임베디드 친구 2025. 1. 5. 10:42
반응형

1. 비지터 패턴이란?

비지터(Visitor) 패턴은 객체의 구조동작을 분리하는 디자인 패턴입니다. 객체 구조는 변하지 않지만, 구조 내에 포함된 요소들에 대해 수행하는 동작(알고리즘)이 자주 변경될 때 유용하게 사용됩니다.

즉, 객체 구조는 그대로 두고 방문자(Visitor) 객체를 통해 새로운 기능을 추가하는 방식입니다.

비지터 패턴은 행동(Behavioral) 패턴 중 하나로, 각 요소의 구체적인 동작을 Visitor 클래스에서 처리합니다.


2. 비지터 패턴의 특징

  • 객체 구조와 연산의 분리: 객체 구조는 고정되지만, 새로운 동작(알고리즘)을 Visitor를 통해 추가할 수 있습니다.
  • 유연한 동작 추가: 기존 클래스의 변경 없이 새로운 기능을 추가할 수 있습니다.
  • 더블 디스패치(Double Dispatch): 두 개의 객체가 서로 상호작용하는 패턴으로, 실행될 메서드를 동적으로 결정합니다.

3. 비지터 패턴의 장점과 단점

장점

  1. 기능 확장성: 기존 클래스의 변경 없이 새로운 동작을 추가할 수 있습니다.
  2. 개방-폐쇄 원칙(OCP) 준수: 객체 구조를 수정하지 않고 확장할 수 있습니다.
  3. 유지보수 용이: 동작(알고리즘)을 Visitor 클래스에 모아 관리할 수 있습니다.

단점

  1. 객체 구조에 종속적: 객체 구조에 변화가 생기면 Visitor 인터페이스와 구현 클래스도 모두 수정해야 합니다.
  2. 복잡한 구조: 요소가 많아질수록 비지터 클래스와 메서드가 복잡해질 수 있습니다.
  3. 더블 디스패치로 인한 번거로움: 두 객체의 상호작용을 처리하기 위해 코드가 번거로울 수 있습니다.

4. 비지터 패턴 클래스 다이어그램

          +------------------+             +-------------------+
          |   Element        |<------------| Visitor           |
          +------------------+             +-------------------+
          | + accept()       |             | + visitA(ElementA)|
          +------------------+             | + visitB(ElementB)|
                  ^                        +-------------------+
                  |
          +------------------+       
          |  ConcreteElementA|
          +------------------+
          | + accept()       |
          +------------------+

          +------------------+
          |  ConcreteElementB|
          +------------------+
          | + accept()       |
          +------------------+

          +-------------------+
          | ConcreteVisitor   |
          +-------------------+
          | + visitA()        |
          | + visitB()        |
          +-------------------+

5. 예제 코드

비지터 패턴 구현 (Java)

// Visitor 인터페이스
interface Visitor {
    void visit(ElementA element);
    void visit(ElementB element);
}

// Element 인터페이스
interface Element {
    void accept(Visitor visitor);
}

// Concrete Element A
class ElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public void operationA() {
        System.out.println("ElementA의 동작 수행");
    }
}

// Concrete Element B
class ElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public void operationB() {
        System.out.println("ElementB의 동작 수행");
    }
}

// Concrete Visitor
class ConcreteVisitor implements Visitor {
    @Override
    public void visit(ElementA element) {
        System.out.println("ConcreteVisitor가 ElementA 방문");
        element.operationA();
    }

    @Override
    public void visit(ElementB element) {
        System.out.println("ConcreteVisitor가 ElementB 방문");
        element.operationB();
    }
}

// 클라이언트 코드
public class VisitorPatternExample {
    public static void main(String[] args) {
        Element[] elements = {new ElementA(), new ElementB()};
        Visitor visitor = new ConcreteVisitor();

        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

실행 결과

ConcreteVisitor가 ElementA 방문
ElementA의 동작 수행
ConcreteVisitor가 ElementB 방문
ElementB의 동작 수행

6. 마무리

비지터 패턴은 객체 구조와 동작을 분리하여 새로운 기능을 추가할 때 유용합니다. 특히 구조는 고정되어 있지만 동작이 자주 바뀌는 경우에 적용하기 좋습니다.

하지만 구조에 변경이 생기면 비지터 패턴의 유연성이 떨어질 수 있으므로, 상황에 맞게 선택하는 것이 중요합니다.

반응형