1. 개요
이 사용방법(예제)에서는 동작 GoF 디자인 패턴 중 하나인 방문자를 소개합니다.
먼저 그 목적과 해결하려는 문제를 설명합니다.
다음으로 방문자의 UML 다이어그램과 실제 예제 구현을 살펴보겠습니다.
2. 방문자 디자인 패턴
방문자 패턴의 목적은 기존 개체 구조를 수정하지 않고 새 작업을 정의하는 것입니다.
구성 요소로 구성된 복합 객체 가 있다고 상상해 보십시오 . 개체의 구조는 고정되어 있습니다. 변경할 수 없거나 새로운 유형의 요소를 구조에 추가할 계획이 없습니다.
이제 기존 클래스를 수정하지 않고 어떻게 새 기능을 코드에 추가할 수 있습니까?
방문자 디자인 패턴이 답이 될 수 있습니다. 간단히 말해서 우리가 해야 할 일은 구조의 각 요소에 방문자 클래스를 받아들이는 함수를 추가하는 것입니다.
그런 식으로 우리의 구성 요소는 방문자 구현이 구성 요소를 "방문"하고 해당 요소에 필요한 작업을 수행할 수 있도록 합니다.
즉, 클래스에서 개체 구조에 적용할 알고리즘을 추출합니다.
결과적으로 우리는 코드를 수정하지 않기 때문에 개방/폐쇄 원칙을 잘 활용할 것이지만 새로운 방문자 구현 을 제공하여 기능을 확장할 수 있습니다 .
3. 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 프로젝트 에서 사용할 수 있습니다 .