2011년 5월 23일 월요일

The Visitor Pattern

23. The Visitor Pattern
Visitor 패턴은 지금까지의 객체 지향 프로그래밍 방식을 바꾸어 다른 클래스의 데이터에 대한 작업을 수행하는 외부 클래스를 작성한다. 이 패턴은 몇 가지 사정 때문에 클래스 계층 구조 안에 위치할 수 없는 다형적 성격의 작업 내용이 있을 경우 유용하다. 예를 들어, 계층 구조가 불 필요하게 클래스의 인터페이스를 산만하게 할 수 있다는 이유 때문에 또는 계층 구조가 작성될 때 해당 작업의 내용을 전혀 감안하지 않는 경우가 있을 때 등이다.

구조


역할
· Visitor 역할 : Visitor 가 하는 역할으 데이터 구조내의 각각의 구체적인 요소에 ‘XXX를 방문했다’라는 visit(XXX)메소드를 선언하는것이다. Visit(XXX)는 XXX를 처리하기 위한 메소드로 실제의 코드는 ConcreteVisitor 역할에 쓰여져있다.
· ConcreteVisitor : ConcreteVisitor는 Visitor 역할의 인터페이스(API)를 구현하는 역할을 한다. Visit(XXX)라는 형태의 메소드를 구현하고 각각의 ConcreteElement역할의 처리를 기술한다.
· Element : Element는 Visitor 역할이 방문할 곳을 나타내는 역할을 하고 있다. 방문자를 받아들이는 accept 메소드를 선언하다. accept 메소드의 인수로는 Visitor 역할이 넘겨진다.
· ConcreteElement : ConcreteElement는 Element 역할의 인터페이스(API)를 구현하는 역할을 한다.
· Objectstructure : ObjectStructure는 Element 역할의 집합을 취급하는 역할을 한다. ConcreteVisitor가 Element를 취급할 수 있는 메소드를 갖추고 있다.

의도
다른 클래스의 데이터에 대한 작업을 수행하는 외부 클래스를 작성
적용시기
  • 다양한 인터페이스를 갖는 수많은 객체들 안에 있는 데이터 작업을 수행하려면, Visitor 패턴의 사용법을 고려해야 한다.
  • Visitor 패턴은 개발자가 위와 같은 클래스들에 대해 서로 무관한 작업들을 수행해야 할 경우에도 유용하게 사용될 수 있다.
  • Visitor 패턴은 개발자가 원하는 소스가 준비되어 있지 않거나 기타 다른 기술적인 이유로 소스 코드의 내용을 변경할 수 없는 경우 클래스 라이브러리나 프레임워크에 함수를 추가할 수 있는 유용한 방법이다. 개발자는 단지 프레임워크의 클래스를 서브클래스화하여 각각의 서브 클래스에 accept 메소드를 추가하면된다.


결론
· Visitor 패턴은 다양한 클래스의 수많은 인스턴스에서 가져온 데이터를 캡슐화할 때 유용하다.
· Visitor 패턴은 클래스의 내용을 변경하지 않고 함ㅅ를 추가할수 있다는 특징이 있다.
· Visitor 패턴은 만능이 아니며, 클래스에서 private 데이터를 얻을수 없다.
· Visitor 패턴은 관련되지 않은 클래스들의 컬렉션에서 데이터를 얻을 수도 있고, 해당 데이터를 사용하여 사용자 프로그램에 대한 전역적인 연산결과를 얻을 수도 있다.
· Visitor 패턴을 사용하여 프로그램에 새로운 작업 내역을 추가하는 것이 쉽다.
· Visitor 패턴은 프로그램의 내용이 더 이상 새로운 클래스의 생성을 필요로 하지 않는 시점에서 강력한 삽입 기능을 발휘할 수 있다.
이중 디스패칭(Double Dispatching)
Visitor 클래스가 작업을 수행하도록 해당 메소드를 실제로는 두번 디스패칭한다.
Visitor 클래스는 주어진 객체의 accept 메소드를 호출하고, accept 메소드는 Visitor 클래스의 visit 메소드를 호출한다. 개발자가 accept 메소드를 보유한 모든 클래스에대해서 작업 내역을 추가할 수 있도록 해주는 것이 이런 쌍방향 호출이다.

일련의 클래스 검색하기
일련의 클래스를 방문할려면, Visitor 클래스로 클래스 인스턴스를 전달할 때 호출되는 프로그램이 방문할 클래스의 모든 인스턴스에 대한 정보를 보유하고 있어야 한다. 또한 해당 요소들을 배열하니 Vector 요쇼와 같은 단순한 구조에저장해야 한다. 또 다른 가능성은 이런 클래스에 대한 Enumeration 결과값을 생성하여 해당 결과값을 Visitor 객체에 전달하는 작업이 될 것이다.


예제소스


예제 소스
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
import java.util.Iterator;
public class ListVisitor extends Visitor {
private String currentdir = "";
public void visit(File file) {
System.out.println(currentdir + "/" + file);
}
public void visit(Directory directory) {
System.out.println(currentdir + "/" + directory);
String savedir = currentdir;
currentdir = currentdir + "/" + directory.getName();
Iterator it = directory.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
entry.accept(this);
}
currentdir = savedir;
}
}
public interface Acceptor {
public abstract void accept(Visitor v);
}
import java.util.Iterator;
public abstract class Entry implements Acceptor {
public abstract String getName();
public abstract int getSize();
public Entry add(Entry entry) throws FileTreatmentException {
throw new FileTreatmentException();
}
public Iterator iterator() throws FileTreatmentException {
throw new FileTreatmentException();
}
public String toString() {
return getName() + " (" + getSize() + ")";
}
}
import java.util.Iterator;
import java.util.Vector;
public class Directory extends Entry {
private String name;
private Vector dir = new Vector();
public Directory(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int getSize() {
int size = 0;
Iterator it = dir.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry) {
dir.add(entry);
return this;
}
public Iterator iterator() {
return dir.iterator();
}
public void accept(Visitor v) {
v.visit(this);
}
}
public class File extends Entry {
private String name;
private 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);
}
}
public class FileTreatmentException extends RuntimeException {
public FileTreatmentException() {
}
public FileTreatmentException(String msg) {
super(msg);
}
}
public class mainClass {
public static void main(String[] args) {
try {
System.out.println("Making root entries...");
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("vi", 10000));
bindir.add(new File("latex", 20000));
rootdir.accept(new ListVisitor());
System.out.println("");
System.out.println("Making user entries...");
Directory Kim = new Directory("Kim");
Directory Lee = new Directory("Lee");
Directory Kang = new Directory("Kang");
usrdir.add(Kim);
usrdir.add(Lee);
usrdir.add(Kang);
Kim.add(new File("diary.html", 100));
Kim.add(new File("Composite.java", 200));
Lee.add(new File("memo.tex", 300));
Kang.add(new File("game.doc", 400));
Kang.add(new File("junk.mail", 500));
rootdir.accept(new ListVisitor());
} catch (FileTreatmentException e) {
e.printStackTrace();
}
}
}


관련패턴
  • Iterator 패턴 : Iterator 패턴도 Visitor 패턴도 어떤 데이터 구조상에서 처리를 실행하는 것이다. Iterator 패턴은 데이터 구조가 보관하고 있는 요소를 하나 하나 얻는데 사용하고, Visitor 패턴은 데이터 구조가 보관하고 있는 요소에 특정의 처리를 가하는데 사용한다.
  • Composite 패턴 : 방문처가 되는 데이터 구조는 Composite 패턴이 되는 경우가 있다.
  • Interpreter 패턴 : Interpreter 패턴에서 Visitor 패턴이 사용되는 경우가 있다.

댓글 없음:

댓글 쓰기

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...