Visitorパターンとは
データ構造と処理を分離する。
データ構造を巡り歩く訪問者を表すクラスを用意し、そのクラスに処理を任せる。
データ構造側は、訪問者を受け入れてやる。
Visitor(訪問者)の役
Visitor役はデータ構造の具体的な要素(ConcreteElement役)ごとに「xxxを訪問した」という
visit(xxx)メソッドを宣言する。
visit(xxx)はxxxを処理するためのメソッドで、実際のコードはConcreteVisit役の側に書かれる。
package visitor;
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
ConcreteVisitor(具体的訪問者)の役
ConcreteVisitor役はVisitor役のインターフェースを実装する。
visit(xxx)という形のメソッドを実装し、個々のConcreteElement役ごとの処理を記述する。
package visitor;
import java.util.Iterator;
public class ListVisitor extends Visitor{
private String currentDir = "";
@Override
public void visit(File file) {
System.out.println(currentDir + "/" + file);
}
public void visit(Directory directory) {
System.out.println(currentDir + "/" + directory);
String savedDir = currentDir;
currentDir = currentDir + "/" + directory.getName();
Iterator<Entry> it = directory.iterator();
while (it.hasNext()) {
Entry entry = it.next();
entry.accept(this);
}
currentDir = savedDir;
}
}
Element(要素)の役
Element役はVisitor役の訪問先を表す役。
訪問者を受け入れるacceptメソッドを宣言する。
acceptメソッドの引数にはVisitor役が渡される。
package visitor;
public interface Element {
public abstract void accept(Visitor v);
}
ConcreteElement(具体的要素)の役
ConcreteElement役はElement役のインターフェースを実装する役。
package visitor;
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;
}
@Override
public void accept(Visitor v) {
v.visit(this);
}
}
package visitor;
import java.util.ArrayList;
import java.util.Iterator;
public class Directory extends Entry{
private String name;
private ArrayList<Entry> directory = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
int size = 0;
Iterator<Entry> it = directory.iterator();
while (it.hasNext()) {
Entry entry = (Entry) it.next();
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry) {
directory.add(entry);
return this;
}
public Iterator<Entry> iterator() {
return directory.iterator();
}
public void accept(Visitor v) {
v.visit(this);
}
}
ObjectStructure(オブジェクト構造)の役
ObjectStructure役はElement役の集合を扱う役。
ConcreteVisitor役が個々のElement役を扱えるようなメソッドを備えている。
この役は上記のDirectoryクラスが該当する。
呼び出し元
package visitor;
public class Main {
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 entryies...");
Directory yuki = new Directory("yuki");
Directory hanako = new Directory("hanako");
Directory tomura = new Directory("tomura");
usrDir.add(yuki);
usrDir.add(hanako);
usrDir.add(tomura);
yuki.add(new File("diary.html", 100));
yuki.add(new File("Composite.java", 200));
hanako.add(new File("memo.tex", 300));
tomura.add(new File("game.doc", 400));
tomura.add(new File("junk.mail", 500));
rootDir.accept(new ListVisitor());
} catch (FileThreatmentException e) {
e.printStackTrace();
}
}
}
実行結果
こちらを参考にさせていただきました。
増補改訂版Java言語で学ぶデザインパターン入門