1. 개요

이 사용방법(예제)에서는 동작 GoF 디자인 패턴 중 하나인 방문자를 소개합니다.

먼저 그 목적과 해결하려는 문제를 설명합니다.

다음으로 방문자의 UML 다이어그램과 실제 예제 구현을 살펴보겠습니다.

2. 방문자 디자인 패턴

방문자 패턴의 목적은 기존 개체 구조를 수정하지 않고 새 작업을 정의하는 것입니다.

구성 요소로 구성된 복합  객체 가 있다고 상상해 보십시오 . 개체의 구조는 고정되어 있습니다. 변경할 수 없거나 새로운 유형의 요소를 구조에 추가할 계획이 없습니다.

이제 기존 클래스를 수정하지 않고 어떻게 새 기능을 코드에 추가할 수 있습니까?

방문자 디자인 패턴이 답이 될 수 있습니다. 간단히 말해서  우리가 해야 할 일은 구조의 각 요소에 방문자 클래스를 받아들이는 함수를 추가하는 것입니다.

그런 식으로 우리의 구성 요소는 방문자 구현이 구성 요소를 "방문"하고 해당 요소에 필요한 작업을 수행할 수 있도록 합니다.

즉, 클래스에서 개체 구조에 적용할 알고리즘을 추출합니다.

결과적으로 우리는 코드를 수정하지 않기 때문에 개방/폐쇄 원칙을 잘 활용할  것이지만 새로운 방문자  구현 을 제공하여 기능을 확장할 수 있습니다 .

3. UML 다이어그램

방문자 UML

위의 UML 다이어그램에는 두 가지 구현 계층, 특수 방문자 및 구체적인 요소가 있습니다.

우선 클라이언트는 방문자 구현을 사용하고 이를 개체 구조에 적용합니다. 복합 개체는 해당 구성 요소를 반복하고 각 구성 요소에 방문자를 적용합니다.

이제 구체적인 요소 (ConcreteElementA ConcreteElementB) 단순히 방문자  를 방문  하도록 허용하는 것과 관련이 있습니다.

마지막으로 이 메서드는 구조의 모든 요소에 대해 동일하며 this 키워드를 통해 방문자의 방문 메서드에  자신을 전달하여  이중 디스패치 를 ​​수행합니다.

4. 시행

우리의 예는  JSON 및 XML 구체적인 요소로 구성된 사용자 지정 문서  개체입니다. 요소에는 공통 추상 슈퍼클래스인  요소가 있습니다.

문서  클래스 :

public class Document extends Element {

    List<Element> elements = new ArrayList<>();

    // ...

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

Element 클래스 에는  방문자  인터페이스  를 허용하는 추상 메서드가  있습니다.

public abstract void accept(Visitor v);

따라서 새 요소를 만들 때 이름을  JsonElement 로 지정하고 이 메서드 구현을 제공해야 합니다.

그러나 방문자 패턴의 특성으로 인해 구현은 동일하므로 대부분의 경우 이미 존재하는 다른 요소에서 상용구 코드를 복사하여 붙여넣어야 합니다.

public class JsonElement extends Element {

    // ...

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

요소는 모든 방문자의 방문을 허용하므로 Document  요소를 처리하려고 하지만 클래스 유형에 따라 각각 다른 방식으로 처리한다고 가정해 보겠습니다  .

따라서 방문자는 주어진 유형에 대해 별도의 메소드를 갖게 됩니다.

public class ElementVisitor implements Visitor {

    @Override
    public void visit(XmlElement xe) {
        System.out.println(
          "processing an XML element with uuid: " + xe.uuid);
    }

    @Override
    public void visit(JsonElement je) {
        System.out.println(
          "processing a JSON element with uuid: " + je.uuid);
    }
}

여기에서 구체적인 방문자는 Element 의 각 유형마다 하나씩 두 가지 메서드를 구현합니다 .

이를 통해 필요한 작업을 수행할 수 있는 구조의 특정 개체에 액세스할 수 있습니다.

5. 테스트

테스트 목적으로 VisitorDemo 클래스 를 살펴보겠습니다 .

public class VisitorDemo {

    public static void main(String[] args) {

        Visitor v = new ElementVisitor();

        Document d = new Document(generateUuid());
        d.elements.add(new JsonElement(generateUuid()));
        d.elements.add(new JsonElement(generateUuid()));
        d.elements.add(new XmlElement(generateUuid()));

        d.accept(v);
    }

    // ...
}

먼저  요소 방문자를 만들고 요소에 적용할 알고리즘을 보유합니다.

다음으로 적절한 구성 요소로 문서  를 설정하고  개체 구조의 모든 요소에서 허용되는 방문자를 적용합니다.

출력은 다음과 같습니다.

processing a JSON element with uuid: fdbc75d0-5067-49df-9567-239f38f01b04
processing a JSON element with uuid: 81e6c856-ddaf-43d5-aec5-8ef977d3745e
processing an XML element with uuid: 091bfcb8-2c68-491a-9308-4ada2687e203

요소  유형 에 따라 방문자가 우리 구조의 각 요소를 방문했음을 보여주고  적절한 메서드로 처리를 전달하고 모든 기본 개체에서 데이터를 검색할 수 있습니다.

6. 단점

각 디자인 패턴과 마찬가지로 방문자도 단점이 있습니다. 특히  개체 구조에 새 요소를 추가해야 하는 경우 코드를 유지 관리하기가 더 어려워집니다.

예를 들어 새 YamlElement 를 추가하는 경우  이 요소를 처리하는 데 필요한 새 메서드로 모든 기존 방문자를 업데이트해야 합니다. 다음으로 구체적인 방문자가 10명 이상인 경우 모든 방문자를 업데이트하는 것이 번거로울 수 있습니다.

이 외에도 이 패턴을 사용할 때 하나의 특정 개체와 관련된 비즈니스 논리가 모든 방문자 구현에 분산됩니다.

7. 결론

방문자 패턴은 작동하는 클래스에서 알고리즘을 분리하는 데 유용합니다. 그 외에도 방문자의 새로운 구현을 제공함으로써 새로운 작업을 더 쉽게 추가할 수 있습니다.

게다가 우리는 구성 요소 인터페이스에 의존하지 않으며 서로 다르다고 해도 괜찮습니다. 구체적인 요소별로 처리하는 별도의 알고리즘이 있기 때문입니다.

또한 방문자는 결국 통과하는 요소를 기반으로 데이터를 집계할 수 있습니다.

방문자 디자인 패턴의 보다 전문화된 버전을 보려면  Java NIO  의 방문자 패턴(JDK에서의 패턴 사용)을 확인하십시오.

평소와 같이 전체 코드는 Github 프로젝트 에서 사용할 수 있습니다 .

Generic footer banner