// 下階層用テンプレート
#topicpath
----
//ここにコンテンツを記述します。
#contents

**概要 [#d2b2b444]

Eclipseのソースコード解析パーサ[[ASTParser>http://help.eclipse.org/help31/index.jsp?topic=/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/ASTParser.html]] を調べてたら、Visitorパタンが使われてたので、いままでめんどくさくってさぼってたVisitorパタンのお勉強をしました。そのメモです。

Visitorパタンは、あるモデルに対して動的にオペレーションを決定するためのパタンです。なんのこっちゃという感じですが、たとえば
-ディレクトリ構造が与えられて、ファイルに対してはそのファイルのサイズを返す、ディレクトリに対してはそのディレクトリ内のファイルサイズの合計を返す
-EclipseのASTparserがパースしたソースコードに対して、JavaDocコメントの場合は○○、フィールドの場合は△△などのオペレーションを行う。

などがありそうです。

**サンプル [#ua4fb3dd]
ディレクトリとファイルの話でサンプルを作ってみます。あるディレクトリ構造が与えられるので、そのディレクトリ構造というモデルに対してVisitorインタフェースの実装クラスをセットします。Visitorインタフェースには、ファイル用のvisitメソッド、ディレクトリ用のvisitメソッドが宣言されているので、それぞれのメソッドを実装すればよいという感じです。

 /**
  * ファイルとディレクトリのインタフェース。
  * 
  * @author Masatomi KINO
  * @version $Revision$
  */
 interface Entry {
   /**
    * Visitorを受け入れるメソッド。だいたいvisitor.visit(this);みたいな処理になる。
    * 
    * @param visitor
    */
   void accept(Visitor visitor);
 
   String getName();
 }


 /**
  * ディレクトリ
  * 
  * @author Masatomi KINO
  * @version $Revision$
  */
 class Directory implements Entry {
   private String name;
   private List<Entry> list = new ArrayList<Entry>();
   public Directory(String name) {
     this.name = name;
   }
   public String getName() {
     return name;
   }
   public void addEntry(Entry e) {
     list.add(e);
   }
   public List<Entry> getFiles() {
     return list;
   }
   public void accept(Visitor visitor) {
     visitor.visit(this); // void visit(Directory directory); が呼ばれてる。
     // ↓ さらに、自分が持ってるEntryたちのacceptも呼んであげる。
     Iterator<Entry> e = list.iterator();
     while (e.hasNext()) {
       Entry element = e.next();
       element.accept(visitor);
     }
   }
 
 }

 /**
  * ファイル
  * 
  * @author Masatomi KINO
  * @version $Revision$
  */
 class File implements Entry {
   private String name;
   public File(String name) {
     this.name = name;
   }
   public String getName() {
     return name;
   }
   public void accept(Visitor visitor) {
     visitor.visit(this); // void visit(File file); が呼ばれてる。
   }
 }

 interface Visitor {
   /**
    * ディレクトリ構造を走査してったときに、 ディレクトリだったら このメソッドが呼ばれる
    * 
    * @param directory
    */
   void visit(Directory directory);
 
   /**
    * ディレクトリ構造を走査してったときに、 ファイルだったら このメソッドが呼ばれる
    * 
    * @param file
    */
   void visit(File file);
 }

さて準備ができました。ではディレクトリ構成を作って、そこにVisitorをセットしようと思います。以下のようにしました。

 class Client {
   public void method() {
     Directory directory = new Directory("ROOT");
     directory.addEntry(new File("ほげ.txt"));
 
     Directory dir = new Directory("child");
     dir.addEntry(new File("ふが.txt"));
     dir.addEntry(new File("ほげほげ.txt"));
 
     directory.addEntry(dir);
 
     // Visitorをセット。
     directory.accept(new Visitor() {
       public void visit(Directory directory) {
         System.out.println(directory.getName());
       }
 
       public void visit(File file) {
         System.out.println(file.getName());
       }
     });
   }
 }
実行結果は以下の通りです。
 ROOT
 ほげ.txt
 child
 ふが.txt
 ほげほげ.txt

**まとめ [#w73fab79]
Visitorパタンを設計する側はともかくとして、使う側つまりVisitorの実装クラスを作る側は、モデル内の各要素(FileとかDirectoryとか)用のメソッド
 void visit(Directory directory);
 void visit(File file);

を実装し、モデルにその実装クラスを
 directory.accept(new Visitor() {.....
でセットしさえすれば、後は勝手にフレームワーク側が実装クラスのメソッドを呼び出してくれるんですね。


**関連リンク [#r6a095f9]
-[[VISITORの骸骨>http://www002.upp.so-net.ne.jp/ys_oota/mdp/Visitor/index.htm]]
-[[Visitor パターン>http://www.ncfreak.com/asato/doc/patterns/visitor.html]]





----
この記事は
#vote(おもしろかった[2],そうでもない[1])
#vote(おもしろかった[3],そうでもない[1])
#topicpath


SIZE(10){現在のアクセス:&counter;}

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS