当您有一个由许多不同的类组成的数据结构并且您有多种算法需要对每个类进行不同的操作时,就会使用访问者模式。在您的示例中,您的 DoInterface 实现仅对一种类型执行一项操作。您唯一要做的就是打印 getS() 的结果,因为您将 o 转换为 T,您只能对 T 类型的类执行此操作。
如果您想将您的界面应用于典型的访问者样式类,那么您的带有 DoInterface.perform 函数的类可能会以一个大的 if else if 语句结束,如下所示:
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”函数来解决这个问题。然后数据结构中的类负责了解访问者上的哪个函数要调用。映射由数据结构的每个类执行一个接受函数,然后回调访问者类。如果访问者上不存在该类型的函数,则会出现编译错误。接受方法如下所示:
@Override
public void accept(FileSystemVisitor v) {
v.visitFile(this);
}
访问者模式的部分问题在于它需要相当多的代码才能在示例中真正做到公正。我认为这就是为什么很多人不明白它的原因,因为很容易被其他代码分心。我创建了一个简单的文件系统示例,希望能更清楚地展示如何使用访问者。它创建一个包含一些文件和目录的组合,然后对层次结构执行两个操作。在实践中,您可能需要两个以上的数据类和两个操作来证明这种模式的合理性,但这只是一个示例。
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);
}
}