23.    The Visitor Pattern

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

 

구조


사용자 삽입 이미지

 

역할

 

·         Visitor역할:Visitor가 하는 역할으 데이터 구조내의 각각의 구체적인 요소에‘XXX를 방문했다라는visit(XXX)메소드를 선언하는것이다. Visit(XXX)XXX를 처리하기 위한 메소드로 실제의 코드는ConcreteVisitor역할에 쓰여져있다.

·         ConcreteVisitor :ConcreteVisitorVisitor역할의 인터페이스(API)를 구현하는 역할을 한다. Visit(XXX)라는 형태의 메소드를 구현하고 각각의ConcreteElement역할의 처리를 기술한다.

·         Element :ElementVisitor역할이 방문할 곳을 나타내는 역할을 하고 있다.방문자를 받아들이는accept메소드를 선언하다. accept메소드의 인수로는Visitor역할이 넘겨진다.

·         ConcreteElement :ConcreteElementElement역할의 인터페이스(API)를 구현하는 역할을 한다.

·         Objectstructure :ObjectStructureElement역할의 집합을 취급하는 역할을 한다. ConcreteVisitorElement를 취급할 수 있는 메소드를 갖추고 있다.

 

의도

다른 클래스의 데이터에 대한 작업을 수행하는 외부 클래스를 작성

 

적용시기

 

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

Posted by 영웅기삼
,