Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用访问者模式和界面之间有什么区别?_Java_Design Patterns_Coding Style_Visitor - Fatal编程技术网

Java 使用访问者模式和界面之间有什么区别?

Java 使用访问者模式和界面之间有什么区别?,java,design-patterns,coding-style,visitor,Java,Design Patterns,Coding Style,Visitor,将访问者设计模式应用于代码与以下方法之间的区别是什么: interface Dointerface { public void perform(Object o); } public class T { private Dointerface d; private String s; public String getS() { return s; } public T(String s) {

将访问者设计模式应用于代码与以下方法之间的区别是什么:

interface Dointerface {
    public void perform(Object o);
}

public class T {
    private Dointerface d;
    private String s;

    public String getS() {
            return s;
    }

    public T(String s) {
            this.s = s;
    }

    public void setInterface(Dointerface d) {
            this.d = d;
    }

    public void perform() {
            d.perform(this);
    }

    public static void main(String[] args) {
            T t = new T("Geonline");
            t.setInterface(new Dointerface() {
                    public void perform(Object o) {
                            T a = (T)o;
                            System.out.println(a.getS());
                    }
            });
            t.perform();
    }
}

我假设通过使用接口,我们并没有真正地分离算法。

我看到的唯一显而易见的事情是,通过存储接口,您可以创建它,因此您必须执行两个操作,而不是一个操作来调用它。我认为,如果您在设置界面后重复执行相同的操作,这可能是有意义的,但我认为您可以坚持使用标准访问者并完成相同的任务。

这有很大的不同

访问者模式使用接口,但其目的是能够对一个或多个类(实现接口的类)执行操作,而无需更改类。因此,实现实际上是“访问”类,并在不修改类的情况下执行其操作

接口是一个基本概念,用于为可能不同的类组提供公共API。接口的典型测试是,共享接口的类至少在一个方面(is-like-a)是相似的,在这些情况下可以这样处理

有两件事:

  • 在您的示例中,需要两种方法。
    perfom
    setInterface
    。对于访问者模式,您只需要一种方法,
    perfom
    ,通常称为
    accept
  • 如果需要多个“执行者”,则必须通过
    setInterface
    方法为每个“执行者”设置执行者。这使得不可能使类保持不变

这些示例中最重要的区别在于,在visitor案例中,您保留了编译时的具体类型“this”。这允许您使用双重分派,其中要调用的方法取决于具体的数据类型和访问者实现。双重分派只是多重分派的一种特殊情况,其中调用的方法取决于接收方和方法的参数类型。Java当然是单分派,但其他一些语言支持多分派

访问者模式背后的基本驱动力是,通过在具体节点上使用接口,需要添加到复合数据结构中的每个操作都必须更改每个节点。访问者模式在节点上使用通用(静态)模式,因此动态添加操作很容易。缺点是修改数据结构(通过添加或删除具体节点)变得更加困难,因为所有操作访问者都会受到影响

通常,这种折衷是一种更好的匹配,因为在数据结构上扩展操作比更改数据结构本身更频繁。以下是我关于如何使用访问者的长篇大论和一系列注意事项:

您可能会问,是否有一种模式允许我们同时执行这两种操作:添加操作或扩展数据结构而不破坏现有代码。这就是菲利普·瓦德勒(Philip Wadler)提出的表达式问题。您可以在此处找到有关此和更多内容的链接:


当您的数据结构由许多不同的类组成,并且有多个算法需要对每个类执行不同的操作时,将使用访问者模式。在您的示例中,DoInterface实现仅对一种类型执行一个操作。您唯一要做的就是打印get()的结果,因为您将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)
            // ...
    }
    @Override
    public void accept(FileSystemVisitor v) {
        v.visitFile(this);
    }
因为它使用对象,所以它将允许具有任何类型的调用方,这些调用方可以创建只在运行时出现的错误。访问者通过为数据结构中的每种类型创建一个“visitType”函数来解决这个问题。然后,数据结构中的类负责知道要调用访问者上的哪个函数。映射由每个数据结构的类执行,这些类实现一个accept函数,然后调用Visitor类。如果访问者上不存在该类型的函数,则会出现编译错误。accept方法如下所示:

    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)
            // ...
    }
    @Override
    public void accept(FileSystemVisitor v) {
        v.visitFile(this);
    }
Visitor模式的部分问题在于,在一个示例中真正做到这一点需要相当多的代码。我认为这就是为什么很多人不理解它,因为它很容易被其他代码分心。我创建了一个简单的文件系统示例,希望能更清楚地展示如何使用访问者。它在中创建一个包含一些文件和目录的组合,然后在层次结构上执行两个操作。实际上,您可能需要两个以上的数据类和两个操作来证明此模式的合理性,但这只是一个示例

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);
        }
    }
公共类访问者示例{
//
公共抽象类FileSystemItem{
公共抽象字符串getName();
公共抽象int getSize();
公开摘要作废接受(FileSystemV);
}
//  
公共抽象类FileSystemContainer扩展了FileSystemEmItem{
受保护的java.util.ArrayList _list=new java.util.ArrayList();
//              
public void addItem(filesystememitem项)
{
_列表。添加(项目);
}
//
公共文件系统getItem(int i)
{
返回_list.get(i);
}
//          
public int getCount(){
返回_list.size();
}
//      
公开摘要作废接受(FileSystemV);
公共抽象字符串getName();
公共抽象int getSize();
}
//