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를 취급할 수 있는 메소드를 갖추고 있다.
의도
다른 클래스의 데이터에 대한 작업을 수행하는 외부 클래스를 작성
적용시기
l 다양한 인터페이스를 갖는 수많은 객체들 안에 있는 데이터 작업을 수행하려면, Visitor패턴의 사용법을 고려해야 한다.
l Visitor패턴은 개발자가 위와 같은 클래스들에 대해 서로 무관한 작업들을 수행해야 할 경우에도 유용하게 사용될 수 있다.
l 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(); } } } |
관련패턴
l Iterator패턴: Iterator패턴도Visitor패턴도 어떤 데이터 구조상에서 처리를 실행하는 것이다. Iterator패턴은 데이터 구조가 보관하고 있는 요소를 하나 하나 얻는데 사용하고, Visitor패턴은 데이터 구조가 보관하고 있는 요소에 특정의 처리를 가하는데 사용한다.
l Composite패턴:방문처가 되는 데이터 구조는Composite패턴이 되는 경우가 있다.
l Interpreter패턴: Interpreter패턴에서Visitor패턴이 사용되는 경우가 있다.
'Programming > Design Pattern' 카테고리의 다른 글
[펌] The State Pattern (0) | 2006.01.21 |
---|---|
[펌] The Template Pattern (0) | 2006.01.21 |
[펌] The Interpreter Pattern (0) | 2006.01.21 |
[펌] The Iterator Pattern (0) | 2006.01.21 |
[펌] The Mediator Pattern (0) | 2006.01.21 |