= Visitor Pattern =
객체 구조를 돌아다니면서 동일한 조작을 반복해서 적용하기. 이 때, '객체 구조'와 '처리(Algorithm)'을 분리하는 것이 핵심. 개방-폐쇄 원칙(OCP, Open-Closed Principle)[(기존 클래스를 수정하지 않고 확장할 수 있도록 하는 것. https://ko.wikipedia.org/wiki/%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84_%EC%9B%90%EC%B9%99)]을 적용한 방법중 하나.
* 언제 사용하는가?
* 데이터 구조 안에 많은 요소가 저장되어 있고, 그 각 요소에 대해서 __다른 종류의 처리(Algorithm)__가 필요한 경우
* 이로써 얻는 이점으로 객체 구조와 처리 역할을 독립적으로 확장 가능!
{{tag>Architecture Modeling Design_Pattern Behavioral}}
시나리오
* [[composite pattern]]의 예제인 "파일 시스템"을 객체 구조(Element, ObjectStructure)로 이용. 여기서 처리 알고리즘을 분리시킨다.
= Visitor =
Method Overloading을 이용하여 각각의 처리할 Logic 수행하는 역할.
package visitor;
import element.objectstructure.concrete.Directory;
import element.objectstructure.concrete.File;
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
package visitor.concrete;
import java.util.Iterator;
import element.objectstructure.Entry;
import element.objectstructure.concrete.Directory;
import element.objectstructure.concrete.File;
import visitor.Visitor;
public class ListVisitor extends Visitor {
private String currentDirectoryName = "";
@Override
public void visit(File file) {
System.out.println(currentDirectoryName + " / " + file);
}
@Override
public void visit(Directory directory) {
System.out.println(currentDirectoryName + " / " + directory);
String tempDirectoryName = currentDirectoryName;
currentDirectoryName = new StringBuilder(currentDirectoryName).append("/").append(directory.getName()).toString();
Iterator it = directory.iterator();
while(it.hasNext()) {
it.next().accept(this);
}
currentDirectoryName = tempDirectoryName;
}
}
= Element =
방문할 객체를 지정하는 역할. accept()로 요청하여 객체 각각의 처리 Logic 수행.
package element;
import visitor.Visitor;
public interface Element {
void accept(Visitor visitor);
}
package element.objectstructure;
import java.util.Iterator;
import element.Element;
import element.objectstructure.exception.FileTreatmentException;
public abstract class Entry implements Element {
public abstract String getName();
public abstract int getSize();
/* Directory에서만 유효 */
public Entry add(Entry entry) throws FileTreatmentException {
throw new FileTreatmentException();
}
/* 객체 구조의 각 요소에 특정한 처리를 실행하는 데 필요 */
/* Directory에서만 유효 */
public Iterator iterator() throws FileTreatmentException {
throw new FileTreatmentException();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getName());
sb.append(" (");
sb.append(getSize());
sb.append(") ");
return sb.toString();
}
}
package element.objectstructure.concrete;
import element.objectstructure.Entry;
import visitor.Visitor;
public class File extends Entry {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
}
package element.objectstructure.concrete;
import java.util.ArrayList;
import java.util.Iterator;
import element.objectstructure.Entry;
import element.objectstructure.exception.FileTreatmentException;
import visitor.Visitor;
public class Directory extends Entry {
private String name;
private ArrayList directories = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return directories.size();
}
@Override
public Entry add(Entry entry) throws FileTreatmentException {
directories.add(entry);
return this;
}
@Override
public Iterator iterator() throws FileTreatmentException {
return directories.iterator();
}
}
package element.objectstructure.exception;
public class FileTreatmentException extends RuntimeException {
public FileTreatmentException() {
}
public FileTreatmentException(String message) {
super(message);
}
}
= Client =
import element.objectstructure.concrete.Directory;
import element.objectstructure.concrete.File;
import visitor.concrete.ListVisitor;
public class Client {
public static void main(String[] args) {
Directory rootdir = new Directory("root");
Directory bindir = new Directory("bin");
Directory tmpdir = new Directory("tmp");
Directory usrdir = new Directory("usr");
rootdir.add(bindir);
rootdir.add(tmpdir);
rootdir.add(usrdir);
bindir.add(new File("sub1", 10000));
bindir.add(new File("sub2", 20000));
rootdir.accept(new ListVisitor());
}
}
/*
/ root (3)
/root / bin (2)
/root/bin / vi (10000)
/root/bin / latex (20000)
/root / tmp (0)
/root / usr (0)
*/