패턴명칭
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