使用访问者模式和接口有什么区别?
将访问者设计模式应用于代码与以下方法有什么区别:
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();
}
}
我假设通过使用接口,我们并没有真正分离算法。
What is the difference between applying the visitor design pattern to your code and the following approach:
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();
}
}
I assume that by using interfaces, we're not really separating the algorithm.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
有相当大的差异。
访问者模式使用接口,但其目的是能够对一个或多个类(实现接口)执行操作,而无需更改类。 因此,实现实际上“访问”该类并在不修改该类的情况下执行其操作。
接口是一个基本概念,用于向可能不同的类组提供通用 API。 对接口的典型测试是共享该接口的类至少在某一方面是相似的(is-like-a),并且在这些情况下可以被视为相似。
这是维基百科上的一个简单示例,显示了几个 Java 访问者。
There is quite a big difference.
The visitor pattern uses interfaces, but its purpose is to be able to perform an operation to one or more classes (who implement an interface) without having to change the classes. Hence, the implementation actually "visits" the class and does its thing without the class being modified.
An interface is a basic concept used to provide a common API to a potentially diverse group of classes. The typical test for an interface is that classes that share it are alike in at least that one respect (is-like-a) and in those cases can be treated as such.
Here is a simple example on wikipedia that shows a couple of visitors in java.
有两件事:
perfom
和setInterface
。 使用访问者模式,您只需要一种方法,即perfom
,通常称为accept
。setInterface
方法为每个“执行者”设置。 这使得你的类不可能变得不可变。Two things:
perfom
and thesetInterface
. With a visitor pattern you would only need one method, theperfom
, usually calledaccept
.setInterface
method- for each. This makes it impossible to make your class immutable.这些示例中最重要的区别是,在访问者情况下,您保留“this”的编译时具体类型。 这允许您使用双重分派,其中要调用的方法取决于具体数据类型和访问者实现。 双重分派只是多重分派的一种特殊情况,其中调用的方法取决于接收者和方法参数的类型。 Java 当然是单分派,但其他一些语言支持多分派。
访问者模式背后的基本驱动力是,通过在具体节点上使用接口,需要添加到复合数据结构的每个操作都必须更改每个节点。 访问者模式在节点上使用通用(静态)模式,以便动态添加操作很容易。 缺点是修改数据结构(通过添加或删除具体节点)变得更加困难,因为所有操作访问者都会受到影响。
一般来说,这种权衡是更好的匹配,因为在数据结构上扩展操作比更改数据结构本身更频繁。 这是我写的一篇关于如何使用访问者和一些注意事项的较长文章:
您可能会问是否存在一种模式可以让我们同时执行这两种操作:添加操作或扩展数据结构而不破坏现有代码。 这就是菲利普·沃德勒 (Philip Wadler) 创造的“表达式问题”。 您可以在这里找到有关此内容和更多内容的一些链接:
The most important difference in these examples is that in the visitor case you retain the compile-time concrete type of "this". This allows you to use double dispatch, where the method to be called is dependent on both the concrete data type and the visitor implementation. Double dispatch is just a special case of multiple dispatch where the method invoked is dependent on the receiver and the types of the parameters to the method. Java is of course single dispatch but some other languages support multiple dispatch.
The basic driving force behind the visitor pattern is that by using interfaces on the concrete nodes, every operation that needs to be added to a composite data structure must change every node. The visitor pattern uses a generic (static) pattern on the nodes so that dynamically adding operations is easy. The downside is that modifying the data structure (by adding or removing concrete nodes) becomes more difficult as all operation visitors are affected.
In general, this trade=off is a better match as it's more frequent to extend operations over a data structure than to change the data structure itself. Here's a lengthier writing of mine on how to use visitors and a bunch of considerations:
You might fairly ask if there is a pattern that allows us to do both: add operations or extend our data structures without breaking existing code. This is known as The Expression Problem as coined by Philip Wadler. You can find some links on this and more here:
当您的数据结构由许多不同的类组成并且您有多个算法需要对每个类进行不同的操作时,请使用访问者模式。 在您的示例中,您的 DoInterface 实现仅对一种类型执行一项操作。 您要做的唯一一件事就是打印 getS() 的结果,并且因为您将 o 转换为 T,所以您只能对 T 类型的类执行此操作。
如果您想将接口应用于典型的访问者样式类,您可以使用您的类DoInterface.perform 函数可能会以一个大的 if else if 语句结束,如下所示:
因为它使用 Object,所以它将允许任何类型的调用者,这些类型可能会创建仅在运行时出现的错误。 访问者通过为数据结构中的每种类型创建一个“visitType”函数来解决这个问题。 然后,数据结构中的类负责了解要调用访问者上的哪个函数。 映射由每个数据结构的类执行,该类实现一个接受函数,然后回调 Visitor 类。 如果访问者上不存在该类型的函数,则会出现编译错误。 接受方法如下所示:
访问者模式的部分问题在于,它需要相当多的代码才能在示例中真正做到这一点。 我认为这就是为什么很多人不明白它的原因,因为很容易被其他代码分散注意力。 我创建了一个简单的文件系统示例,希望能够更清楚地展示如何使用访问者。 它创建一个包含一些文件和目录的组合,然后在层次结构上执行两个操作。 在实践中,您可能需要两个以上的数据类和两个操作来证明此模式的合理性,但这只是一个示例。
A Visitor pattern is used when you have a data structure made up of many different classes and you have multiple algorithms that require a different operation for each class. In your example your DoInterface implementation only does one operation on one type. The only thing you do is print the result of getS() and because you cast o to T you can only do this to classes of type T.
If you wanted to apply your interface to a typical visitor style class you your the class with your DoInterface.perform function would likely end up with a big if else if statement in it something like this:
Because this uses Object it will allow callers with any type which can create errors which will only show up at runtime. A Visitor gets around this by creating a “visitType” function for each type in the data structure. The classes in the data structure are then responsible for knowing which function on the visitor to call. The mapping is performed by each of the data structure’s classes implementing an accept function that then calls back on the Visitor class. If the function for the type does not exist on the visitor you get a compile error. The accept method looks like this:
Part of the trouble with the Visitor pattern is that it takes quite a lot of code to really do it justice in a sample. I think this is why a lot of people don't get it as it is easy to get distracted by the other code. I have created a simple file system sample that hopefully shows how to use a visitor more clearly. It creates a composite with some files and directories in and then performs two operations on the hierarchy. In practice you would probably want more than two data classes and two operations to justify this pattern but this is only an example.
我看到的唯一显而易见的事情是,通过存储接口,您必须执行两项操作而不是一项操作才能调用它。 我认为,如果您在设置界面后重复执行相同的操作,那么这可能是有意义的,但我认为您可以坚持使用标准访问者并完成相同的事情。
The only thing that I see that is readily obvious is that by storing the interface, you make it so you have to do two operations rather than one to invoke it. I suppose that this could make sense if you are repeatedly going to perform the same action once the interface is set, but I think you could stick with the standard Visitor and accomplish the same thing.