알고리즘들을 그들이 작동하는 객체들로부터 분리할 수 있도록 하는 행동 디자인 패턴
💡 Visitor패턴은 객체 구조를 변경하지 않고도 새로운 기능을 추가

각 노드는 산업, 관광 지역들 이다.
각 노드에는 도로가 깔려있으면 이어진다.

여기서 각 노드,연결 정보를 xml로 내보내야 하는 상황이 왔다!
각 노드 클래스에 export(내보내기) 메서드를 추가한다음 재귀호출을 하면 될것 같았다.
class Node{
pubilc void export(){
if (...) return;
this.nextNode().export();
}
}
하지만 시스템 설계자는 Node 클래스가 변경되는것을 원치 않았다..
Node 클래스가 변경되는 것은 어디서 생길지 모르는 오류를 감수해야하기도 하고새로운 행동을 기존 클래스에 통합하는 것이 아닌 visitor 라는 별도의 클래스에 배치 해보자
일단 visitor 클래스를 만들어 보자
class ExportVisitor implements Visitor {
method doForCity(City c) { "city는 이렇게 하자~" }
method doForIndustry(Industry f) { "산업단지는 이렇게 하자~" }
method doForSightSeeing(SightSeeing ss) { "관광지는 이렇게 하자~" }
// …
}
도시, 산업단지, 관광지 등 각 노드에 맞는 내보내기 메서드를 구현한 ExportVisitor이다.
각 노드를 가지고있는 graph 변수에 대해 export를 진행한다면 이렇게 가능할것 같다.
Visitor exportVisitor = new ExportVisitor();
for(Node node : graph){
if (node instanceof City) exportVisitor.doForCity(node);
if (node instanceof Industry) exportVisitor.doForIndustry(node);
if (node instanceof SightSeeing) exportVisitor.doForSightSeeing(node);
//...
}
하지만 이건 너무 비효율 적이다.
비지터가 호출할 메서드를 고르는게 아닌 각 클래스가 비지터를 호출하면 될것 같다.
class City extends Node {
...
public void accept(Visitor visitor){
visitor.doForCity(this);
}
}
다른 클래스들도 visitor을 받아 자신(this)를 넣어 visitor 메서드를 호출하는 메서드를 구현한다.
그렇다면 위 foreach 문은 이렇게 바뀐다.
Visitor exportVisitor = new ExportVisitor();
for(Node node : graph){
node.accept(exportVisitor);
}
우리는 각 Node 클래스를 바꾸고 싶지 않아서 Visitor 패턴을 사용했지만 accept 라는 메서드가 추가됐다.
위 의문점에서 많은게 잘못 되었다고 생각했다.
하지만 더욱 찾아본 결과 Visitor의 진정한 의미에 대해 깨닫게 되고 의문점은 해소 되었다.
아이템 객체인 Book, Pen 이 있다고 해보자
public class Book implements Item {
private int price;
@Override
public void accept(Visitor visitor) {
visitor.doForBook(this);
}
public int getPrice() {};
public void setPrice(int price) {};
}
public class Pen implements Item {
private int price;
@Override
public void accept(Visitor visitor) {
visitor.doForPen(this);
}
public int getPrice() {};
public void setPrice(int price) {};
}
이때 Visitor는 알고리즘을 포함하는 여러개가 나올수 있다.
전체가격을 조회하는 PriceCalculator
public class PriceCalculator implements Visitor {
@Override
public void doForBook(Book book) {
System.out.println("book price : " + book.getPrice());
}
@Override
public void doForPen(Pen pen) {
System.out.println("pen price : " + pen.getPrice());
}
}
세금이 붙은 가격을 조회하는 TaxCalculator
public class TaxCalculator implements Visitor {
private float tax = 1.2f;
@Override
public void doForBook(Book book) {
System.out.println("Book tax : " + book.getPrice() * tax);
}
@Override
public void doForPen(Pen pen) {
System.out.println("Pen tax : " + pen.getPrice() * tax);
}
}
이렇게 다양한 알고리즘이 생겨도 아이템 클래스는 수정되지않는다
각 가격을 조회하고 싶다면 직접 visitor을 넘기면 된다.
PriceCalculator priceCalculatorVisitor = new PriceCalculator();
TaxCalculator taxCalculatorVisitor = new TaxCalculator();
ArrayList<Item> itemBox = new ArrayList<Item>();
Book book = new Book();
book.setPrice(1000);
itemBox.add(book);
Pen pen = new Pen();
pen.setPrice(500);
itemBox.add(pen);
for (Item i : itemBox) {
i.accept(priceCalculatorVisitor);
}
for (Item i : itemBox) {
i.accept(taxCalculatorVisitor);
}
참고자료