Top / デザインパターン / Visitor

概要

Eclipseのソースコード解析パーサASTParser を調べてたら、Visitorパタンが使われてたので、いままでめんどくさくってさぼってたVisitorパタンのお勉強をしました。そのメモです。

Visitorパタンは、あるモデルに対して動的にオペレーションを決定するためのパタンです。なんのこっちゃという感じですが、たとえば

などがありそうです。

サンプル

ディレクトリとファイルの話でサンプルを作ってみます。あるディレクトリ構造が与えられるので、そのディレクトリ構造というモデルに対して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

まとめ

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

void visit(Directory directory);
void visit(File file);

を実装し、モデルにその実装クラスを

directory.accept(new Visitor() {.....

でセットしさえすれば、後は勝手にフレームワーク側が実装クラスのメソッドを呼び出してくれるんですね。

関連リンク


この記事は

選択肢 投票
おもしろかった 1  
そうでもない 1  

Top / デザインパターン / Visitor

現在のアクセス:4713


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS