우리가 일반적으로 데이터의 정의를 하고 처리를 해줄 때 일반적으로는 데이터가 명시되어있는 곳에 처리 해줍니다. 만약 데이터의 종류가 여러 개라면 데이터 구조의 클래스를 수정해야 합니다.
Visitor Pattern은 위와 같은 상황에서 data의 명시와 처리하는 과정을 분리합니다. 그리고 방문자를 만들어서 데이터의 처리를 위임합니다.
이렇게 따로 분리를 해준다면 데이터의 구조를 건드리지 않고도 새로운 동작을 추가할 수 있습니다.
이전의 Composite Pattern의 예제를 빌려와서 사용해보겠습니다.
Visitor.java
public abstract class Visitor{ public abstract void visit(File file); public abstract void visit(Directory directory); }
데이터의 종류별로 visit 함수를 만들어 줍니다.
Element.java
public interface Element{ public abstract void accept(Visitor v); }
각 데이터 별로 방문할 수 있는 메소드를 만들어준다.
Entry.java
public abstract class Entry implements Element{ public abstract String getName(); public abstract int getSize(); public String toString(){ return getName()+" ("+getSize()+")"; } }
Composite Pattern에서 사용되었던 Entry와 달리 데이터를 처리해주는 메소드가 없습니다.
File.java
public class File extends Entry{ String name; int size; public File(String name, int size){ this.name = name; this.size = size; } public String getName(){ return name; } public int getSize(){ return size; } public void accept(Visitor v){ v.visit(this); } }
accept를 구현해줍니다.
Directory.java
import java.util.ArrayList; import java.util.Iterator; public class Directory extends Entry{ String name; ArrayList dirs = new ArrayList(); public Directory(String name){ this.name = name; } public String getName(){ return name; } public int getSize(){ Iterator it = iterator(); int size = 0; while(it.hasNext()){ Entry entry = (Entry)it.next(); size+=entry.getSize(); } return size; } public Iterator iterator(){ return dirs.iterator(); }
public void add(Entry entry){ dirs.add(entry); } public void accept(Visitor v){ v.visit(this); } }
visitor에서 사용해주기 위해 iterator를 호출해주는 method를 만들어 주었습니다.
ListVisit.java
import java.util.Iterator; public class ListVisit extends Visitor{ String currentDirectory=""; String savedString =""; public void visit(File file){ System.out.println(currentDirectory+"/"+file); } public void visit(Directory directory){ System.out.println(currentDirectory+"/"+directory); savedString = currentDirectory; currentDirectory = "/"+directory.getName(); Iterator it = directory.iterator(); while(it.hasNext()){ Entry entry = (Entry)it.next(); entry.accept(this); } currentDirectory = savedString; } }
각 타입별로 방문자가 방문했을 때 데이터를 어떻게 처리할지 결정하는 클래스이다. 기본적인 로직은 Composite일때랑 같다. Composite의 로직을 따로 떼어 놓는 것과 같다.
main.java
public class Main{ public static void main(String[] args){ Directory root = new Directory("root"); Directory bin = new Directory("bin"); Directory tmp = new Directory("tmp"); root.add(bin); root.add(tmp); bin.add(new File("vi", 10000)); bin.add(new File("latext", 20000)); root.accept(new ListVisit()); } }
ListVisit를 등록시켜주어서 ListVisit에 등록된 visit메소드가 실행되게 만들어준다.