ビジターパターンは、多くの異なるクラスで構成されるデータ構造があり、クラスごとに異なる操作を必要とする複数のアルゴリズムがある場合に使用されます。この例では、DoInterface実装は1つのタイプに対して1つの操作のみを実行します。getS()の結果を出力するだけで、oをTにキャストするため、これはタイプTのクラスに対してのみ実行できます。
インターフェイスを一般的なビジタースタイルのクラスに適用したい場合、DoInterface.perform関数を使用したクラスは、次のようなifelseifステートメントで大きくなる可能性があります。
public void visit(Object o) {
if (o instanceof File)
visitFile((File)o);
else if (o instanceof Directory)
visitDirectory((Directory)o);
else if (o instanceof X)
// ...
}
これはObjectを使用するため、実行時にのみ表示されるエラーを作成する可能性のある任意のタイプの呼び出し元を許可します。訪問者は、データ構造のタイプごとに「visitType」関数を作成することでこれを回避します。データ構造内のクラスは、訪問者のどの関数を呼び出すかを知る責任があります。マッピングは、Visitorクラスをコールバックするaccept関数を実装するデータ構造の各クラスによって実行されます。タイプの関数がビジターに存在しない場合、コンパイルエラーが発生します。acceptメソッドは次のようになります。
@Override
public void accept(FileSystemVisitor v) {
v.visitFile(this);
}
ビジターパターンの問題の一部は、サンプルで実際にそれを正しく行うには非常に多くのコードが必要になることです。他のコードに気を取られやすいので、多くの人がそれを受け取らないのはこのためだと思います。訪問者をより明確に使用する方法を示す簡単なファイルシステムサンプルを作成しました。いくつかのファイルとディレクトリを含むコンポジットを作成し、階層に対して2つの操作を実行します。実際には、このパターンを正当化するために3つ以上のデータクラスと2つの操作が必要になる可能性がありますが、これは単なる例です。
public class VisitorSample {
//
public abstract class FileSystemItem {
public abstract String getName();
public abstract int getSize();
public abstract void accept(FileSystemVisitor v);
}
//
public abstract class FileSystemItemContainer extends FileSystemItem {
protected java.util.ArrayList<FileSystemItem> _list = new java.util.ArrayList<FileSystemItem>();
//
public void addItem(FileSystemItem item)
{
_list.add(item);
}
//
public FileSystemItem getItem(int i)
{
return _list.get(i);
}
//
public int getCount() {
return _list.size();
}
//
public abstract void accept(FileSystemVisitor v);
public abstract String getName();
public abstract int getSize();
}
//
public class File extends FileSystemItem {
//
public String _name;
public int _size;
//
public File(String name, int size) {
_name = name;
_size = size;
}
//
@Override
public void accept(FileSystemVisitor v) {
v.visitFile(this);
}
//
@Override
public String getName() {
return _name;
}
//
@Override
public int getSize() {
return _size;
}
}
//
public class Directory extends FileSystemItemContainer {
//
private String _name;
//
public Directory(String name) {
_name = name;
}
//
@Override
public void accept(FileSystemVisitor v) {
v.visitDirectory(this);
}
//
@Override
public String getName() {
return _name;
}
//
@Override
public int getSize() {
int size = 0;
for (int i = 0; i < _list.size(); i++)
{
size += _list.get(i).getSize();
}
return size;
}
}
//
public abstract class FileSystemVisitor {
//
public void visitFile(File f) { }
public void visitDirectory(Directory d) { }
//
public void vistChildren(FileSystemItemContainer c) {
for (int i = 0; i < c.getCount(); i++)
{
c.getItem(i).accept(this);
}
}
}
//
public class ListingVisitor extends FileSystemVisitor {
//
private int _indent = 0;
//
@Override
public void visitFile(File f) {
for (int i = 0; i < _indent; i++)
System.out.print(" ");
System.out.print("~");
System.out.print(f.getName());
System.out.print(":");
System.out.println(f.getSize());
}
//
@Override
public void visitDirectory(Directory d) {
for (int i = 0; i < _indent; i++)
System.out.print(" ");
System.out.print("\\");
System.out.print(d.getName());
System.out.println("\\");
//
_indent += 3;
vistChildren(d);
_indent -= 3;
}
}
//
public class XmlVisitor extends FileSystemVisitor {
//
private int _indent = 0;
//
@Override
public void visitFile(File f) {
for (int i = 0; i < _indent; i++)
System.out.print(" ");
System.out.print("<file name=\"");
System.out.print(f.getName());
System.out.print("\" size=\"");
System.out.print(f.getSize());
System.out.println("\" />");
}
//
@Override
public void visitDirectory(Directory d) {
for (int i = 0; i < _indent; i++)
System.out.print(" ");
System.out.print("<directory name=\"");
System.out.print(d.getName());
System.out.print("\" size=\"");
System.out.print(d.getSize());
System.out.println("\">");
//
_indent += 4;
vistChildren(d);
_indent -= 4;
//
for (int i = 0; i < _indent; i++)
System.out.print(" ");
System.out.println("</directory>");
}
}
//
public static void main(String[] args) {
VisitorSample s = new VisitorSample();
//
Directory root = s.new Directory("root");
root.addItem(s.new File("FileA", 163));
root.addItem(s.new File("FileB", 760));
Directory sub = s.new Directory("sub");
root.addItem(sub);
sub.addItem(s.new File("FileC", 401));
sub.addItem(s.new File("FileD", 543));
Directory subB = s.new Directory("subB");
root.addItem(subB);
subB.addItem(s.new File("FileE", 928));
subB.addItem(s.new File("FileF", 238));
//
XmlVisitor xmlVisitor = s.new XmlVisitor();
root.accept(xmlVisitor);
//
ListingVisitor listing = s.new ListingVisitor();
root.accept(listing);
}
}