我知道这是老生常谈的领域,但我有一个具体的问题.我保证.

在静态类型、面向对象的世界里花了很少时间,我最近在阅读Crafting Interpreters篇文章时遇到了这种设计模式.虽然我理解这种模式允许在一组定义良好的现有types(类)上使用可扩展的behavior(方法),但我并不完全理解它作为双重分派问题解决方案的特性,至少在没有一些额外假设的情况下是如此.我认为这更像是对expression problem的一种折衷,在expression problem中,你用封闭的类型来交换开放的方法.

在我见过的大多数例子中,你最终都会得到这样的结果(无耻地从令人敬畏的Clojure Design Patterns强中窃取)

public interface Visitor {
  void visit(Activity a);
  void visit(Message m);
}

public class PDFVisitor implements Visitor {
  @Override
  public void visit(Activity a) {
    PDFExporter.export(a);
  }

  @Override
  public void visit(Message m) {
    PDFExporter.export(m);
  }
}

public abstract class Item {
  abstract void accept(Visitor v);
}

class Message extends Item {
  @Override
  void accept(Visitor v) {
    v.visit(this);
  }
}

class Activity extends Item {
  @Override
  void accept(Visitor v) {
    v.visit(this);
  }
}

Item i = new Message();
Visitor v = new PDFVisitor(); 
i.accept(v);

这里我们有一组类型(消息和活动),它们可能是关闭的或不经常更改的,以及一组我们希望为扩展(访问者)开放的方法.现在我感到困惑的是,在大多数示例中,它们将向您展示如何在不触及现有类的情况下实现其他访问者,例如,如下所示:

public class XMLVisitor implements Visitor {
  @Override
  public void visit(Activity a) {
    XMLExporter.export(a);
  }

  @Override
  public void visit(Message m) {
    XMLExporter.export(m);
  }
}

然后用一些粗俗的言辞暗示这是"双重派遣",而事实并非如此.

当我想到双重分派时,我会想到一个按两个参数类型进行分派的函数.一种行为,两种类型.

export(Activity,XML)
export(Activity,PDF)
export(Message,XML)
export(Message,PDF)

对我来说,这与访问者模式略有不同,访问者模式允许将任何行为集扩展到现有类,但这些行为不一定都表示与上面四个export个示例中相同的行为-它们可以是任何东西.如果我们增加另一个访问者,它可能代表出口,但也可能不是.从API层,您只需调用accept个方法,并相信传入的Visitor会做您想做的事情,不管是什么.

我是不是看错了方向?

推荐答案

@user207421的 comments 恰到好处.如果一种语言本身不支持双重分派,则任何设计模式都无法更改该语言来实现这一点.模式只是提供了一种替代方案,可以解决在另一种语言中可能会应用双重分派的一些问题.

已经了解双重分派的访问者模式的学习人员可以通过这样的解释来帮助他们:"访问者解决了与通过双重分派解决的问题类似的一组问题".不幸的是,这种解释经常被简化为"游客实行双重调度",这不是真的.

你已经认识到这一点,这意味着你已经对这两个概念有了坚实的理解.

Java相关问答推荐

Java FFM,如何将Java对象与C struct 链接起来?

使用hibiter中特定字段的where条款自定义映射

Android视图覆盖不阻止点击它后面的控件

Jooq外键关系

Java中是否有某种类型的池可以避免重复最近的算术运算?

这是什么Java构造`(InputStream Is)->;()->;{}`

解释左移在Java中的工作原理

Mac上的全屏截图在使用JavaFX时不能正常工作吗?

在Java 17中使用两个十进制数字分析时间时出错,但在Java 8中成功

如何在代码中将行呈现在矩形前面?

Lombok@Nonnull是否也对供应商有影响?

Regex以查找不包含捕获组的行

错误:未找到扩展元素在JBossEAP 7.2中安装FUSE时出错

在一行中检索字符分隔字符串的第n个值

在Oracle中调用输出参数在索引处缺少IN或OUT参数的函数

在Eclipse中可以使用外部字体吗?

使用@ExceptionHandler的GlobalExceptionHandler还是来自服务器的REST应答的ResponseEntity?

如何修复Spring Boot应用程序中的RestDocumentationGenerationException:java.io.FileNotFoundException:/curl-request.adoc(只读文件系统)?

谷歌应用引擎本地服务器赢得';t在eclipse上运行

读取ConcurrentHashMap中的可变对象