개발/디자인패턴

[Java][디자인 패턴] 16. 방문자 패턴 (Visitor Pattern)

nova_dev 2022. 3. 20. 00:00
반응형

디자인패턴
[Java][디자인 패턴] 16. 방문자 패턴 (Visitor Pattern)

방문자(Visitor) 패턴은 방문자(Visitor)와 방문 공간(Visitable)을 분리하여 공통된 객체의 데이터 구조와 처리를 분리하는 패턴이다.

 방문자 패턴이란?

  • OCP(개방 폐쇄 원칙)을 위한 디자인 패턴으로, 기존 클래스를 수정하지 않고 새로운 기능을 코드에 추가하기 위해 사용하는 패턴이다.
  • 각 Element 구조에 Visitor 클래스를 accept 함으로써 새로운 함수를 추가할 수 있다.
  • 그렇게 하면 Element를 통해 ConcreteVisitor가 Element를 ‘visit’ 하고 해당 요소에 대해 필요한 작업을 수행할 수 있다.
  • 결과적으로 코드를 수정하지 않고 OCP를 준수하면서 새로운 ConcreteVisitor를 구현하여 기능을 확장할 수 있다.

 

 

 방문자 패턴 구조

  • Visitor
    • 방문자 클래스의 인터페이스
    • visit(element)를 공용 인터페이스로 사용한다. element는 공용 공간이다.
  • ConcreteVisitor
    • Visitor 구체 클래스
  • Element
    • 방문 공간 클래스의 인터페이스
    • accept(visitor)를 공용 인터페이스로 쓴다. visitor는 방문자다.
    • 내부적으로 Visitor.visit(this)를 호출한다.
  • ConcreteElement
    • Element 구체 클래스

 방문자 패턴 예시 코드 구조

이번 예시코드는 방문자 패턴의 구조를 언제 사용할만한지 잘 생각이 안나서 baeldung 코드를 참고했다.
원본 링크는 아래 있으니 참고하자.
https://www.baeldung.com/java-visitor-pattern

 방문자 패턴 코드

1. Element 추상 클래스

public abstract class Element {
    public String uuid;

    protected Element(String uuid) {
        this.uuid = uuid;
    }

    public abstract void accept(Visitor v);
}

2. Element 추상클래스를 상속받은 JsonElement, XmlElement 클래스

public class JsonElement extends Element {
    public JsonElement(String uuid) {
        super(uuid);
    }

    @Override
    public void accept(Visitor v) {
        v.visit(this);
    }
}
public class XmlElement extends Element {
    public XmlElement(String uuid) {
        super(uuid);
    }

    @Override
    public void accept(Visitor v) {
        v.visit(this);
    }
}

3. Element를 상속받은 Document 클래스
각 Element가 어떤 것들이 있는지 리스트로 정보를 가지고 있는 Document 클래스

public class Document extends Element {

    private final List<Element> elementList;

    public Document(String uuid) {
        super(uuid);
        this.elementList = new ArrayList<>();
    }

    @Override
    public void accept(Visitor v) {
        for (Element e : this.elementList) {
            e.accept(v);
        }
    }

    public List<Element> getElementList() {
        return elementList;
    }
}

4. Visitor 인터페이스

public interface Visitor {
    void visit(Element element);
}

5. Element Visitor 클래스

public class ElementVisitor implements Visitor {

    @Override
    public void visit(Element element) {
        System.out.println("Element [" + element.uuid + "]");
    }

}

6. 테스트 코드

class VisitorTest {
    @Test
    @DisplayName("Visitor 테스트")
    void visitorTest() {
        Visitor visitor = new ElementVisitor();
        Document document = new Document(generateUuid());
        document.getElementList().add(new JsonElement(generateUuid()));
        document.getElementList().add(new JsonElement(generateUuid()));
        document.getElementList().add(new XmlElement(generateUuid()));

        document.accept(visitor);
    }

    private String generateUuid() {
        return UUID.randomUUID().toString();
    }
}

결과

Element [00cddc7a-e86f-4643-acea-7e6e609736c8]
Element [8d1695b3-59f6-4160-ba0b-30ef46c24328]
Element [c23bd59e-39ff-42ca-9240-9840e9aecdea]

 

 방문자 패턴의 단점

단점

  • 객체 구조에 새로운 Element를 추가해야 하는 경우 방문자 패턴을 사용하면 코드를 유지 관리하기 더 어려워진다.
  • 예를 들어 새로 YamlElement를 추가해야 할 경우 이 요소를 처리하는 데 필요한 새 메서드로 인해 모든 기존 방문자를 업데이트 해야 한다. ConcreteVisitor가 여러개일수록 모든 방문자를 업데이트 하는 것이 번거롭다.
  • 하나의 특정 개체와 관련된 비즈니스 로직이 모든 방문자 구현에 퍼진다는 단점이 있다.

 

 

 

 코드 출처

https://www.baeldung.com/java-visitor-pattern

참고 자료

 

 

 

 

 

반응형