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() {.....
でセットしさえすれば、後は勝手にフレームワーク側が実装クラスのメソッドを呼び出してくれるんですね。
この記事は
現在のアクセス:5388