패턴명칭
Visitor
필요한 상황
데이터와 이 데이터의 처리를 분리하여 구현하고자 할때 사용되는 패턴입니다. 데이터는 Composite 패턴으로 구현되므로 집합을 구성하는 단일 요소 역시 집합으로 저장될 수 있습니다. 이러한 집합에 대한 집합으로 구성된 데이터를 처리하는 로직을 독립적으로 구현할 수 있습니다.
예제 코드

Visitor 인터페이스는 데이터를 처리하는 클래스가 구현해야할 공통 인터페이스입니다. 코드는 다음과 같습니다.
package tstThread;
public interface Visitor {
void visit(Unit unit);
}
이 Visitor를 구현하는 클래스로는 SumVisitor, MaxVisitor과 위의 클래스 다이어그램에는 표시되어 있지 않지만 MinVisitor, AvgVisitor이 있습니다. 이 네 클래스는 각각 데이터의 총합 계산, 데이터 중 최대값 파악, 데이터 중 최소값 파악, 데이터의 평균값 계산입니다. 데이터는 Unit 인터페이스를 구현해야 하며 코드는 다음과 같습니다.
package tstThread;
public interface Unit {
void accept(Visitor visitor);
}
이 Unit 인터페이스를 구현하는 Item에는 하나의 정수값이 저장되며 ItemList는 여러개의 Unit 객체를 담을 수 있습니다. 먼저 Item 클래스는 다음과 같습니다.
package tstThread;
public class Item implements Unit {
private int value;
public Item(int value) {
this.value = value;
}
public int getValue() {
return value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
ItemList 클래스는 다음과 같습니다.
package tstThread;
import java.util.ArrayList;
import java.util.Iterator;
public class ItemList implements Unit {
private String name;
private ArrayList<Unit> list = new ArrayList<Unit>();
public ItemList(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void add(Unit unit) {
list.add(unit);
}
@Override
public void accept(Visitor visitor) {
Iterator<Unit> iter = list.iterator();
while(iter.hasNext()) {
Unit unit = iter.next();
visitor.visit(unit);
}
}
}
이제 이러한 데이터를 처리하는 Visitor 인터페이스의 구현 클래스를 살펴보겠습니다. 먼저 SumVisitor 클래스입니다.
package tstThread;
public class SumVisitor implements Visitor {
private int sum = 0;
public int getValue() {
return sum;
}
@Override
public void visit(Unit unit) {
if(unit instanceof Item) {
sum += ((Item)unit).getValue();
} else {
unit.accept(this);
}
}
}
다음은 MaxVisitor 클래스입니다.
package tstThread;
public class MaxVisitor implements Visitor {
private int max = Integer.MIN_VALUE;
private String name = null;
private String visitedName = null;
public int getValue() {
return max;
}
public String getName() {
return name;
}
@Override
public void visit(Unit unit) {
if(unit instanceof Item) {
int value = ((Item)unit).getValue();
if(value > max) {
max = value;
name = visitedName;
}
} else {
visitedName = ((ItemList)unit).getName();
unit.accept(this);
}
}
}
다음은 MinVisitor 클래스입니다.
package tstThread;
public class MinVisitor implements Visitor {
private int min = Integer.MAX_VALUE;
private String name = null;
private String visitedName = null;
public int getValue() {
return min;
}
public String getName() {
return name;
}
@Override
public void visit(Unit unit) {
if(unit instanceof Item) {
int value = ((Item)unit).getValue();
if(value < min) {
name = visitedName;
min = value;
}
} else {
visitedName = ((ItemList)unit).getName();
unit.accept(this);
}
}
}
다음은 AvgVisitor 클래스입니다.
package tstThread;
public class AvgVisitor implements Visitor {
private int sum = 0;
private int count = 0;
public double getValue() {
return sum / count;
}
@Override
public void visit(Unit unit) {
if(unit instanceof Item) {
sum += ((Item)unit ).getValue();
count++;
} else {
unit.accept(this);
}
}
}
지금까지의 클래스를 사용하는 예제 코드는 다음과 같습니다.
package tstThread;
public class Main {
public static void main(String[] args) {
ItemList root = new ItemList("root");
root.add(new Item(10));
root.add(new Item(20));
root.add(new Item(40));
ItemList subList1 = new ItemList("sub1");
subList1.add(new Item(5));
subList1.add(new Item(16));
subList1.add(new Item(36));
ItemList subList2 = new ItemList("sub2");
subList2.add(new Item(50));
subList2.add(new Item(70));
ItemList subList3 = new ItemList("sub2-sub");
subList3.add(new Item(8));
subList3.add(new Item(21));
subList3.add(new Item(37));
root.add(subList1);
root.add(subList2);
subList2.add(subList3);
SumVisitor sum = new SumVisitor();
root.accept(sum);
System.out.println("Sum: " + sum.getValue());
MaxVisitor max = new MaxVisitor();
root.accept(max);
System.out.println("Max: " + max.getValue() + " @" + max.getName());
MinVisitor min = new MinVisitor();
root.accept(min);
System.out.println("Min: " + min.getValue() + " @" + min.getName());
AvgVisitor avg = new AvgVisitor();
root.accept(avg);
System.out.println("Avg: " + avg.getValue());
}
}
실행 결과는 다음과 같습니다.
Sum: 313
Max: 70 @sub2
Min: 5 @sub1
Avg: 28.0
이 글은 소프트웨어 설계의 기반이 되는 GoF의 디자인패턴에 대한 강의자료입니다. 완전한 실습을 위해 이 글에서 소개하는 클래스 다이어그램과 예제 코드는 완전하게 실행되도록 제공되지만, 상대적으로 예제 코드와 관련된 설명이 함축적으로 제공되고 있습니다. 이 글에 대해 궁금한 점이 있으면 댓글을 통해 남겨주시기 바랍니다.