在Scala中使用Visitor Pattern是否有用例?
每次我在Java中使用访问者模式时,我是否应该在Scala中使用Pattern Matching?
答案 0 :(得分:45)
是的,您应该从模式匹配开始,而不是访问者模式。见interview with Martin Odersky(我的重点):
因此,适合工作的工具实际上取决于您的方向 想扩展。如果您想扩展新数据,请选择 使用虚方法的经典面向对象方法。如果你想 保持数据固定并随新操作扩展,然后是模式 是一个更好的适合。实际上有一种设计模式 - 不是 混淆了模式匹配 - 在面向对象编程中称为 访客模式,可以代表我们做的一些事情 基于虚方法的面向对象方式的模式匹配 调度。 但在实际使用中,访客模式非常笨重。您 不能用模式匹配做很多很容易的事情。 你最终会有非常沉重的访客。而且事实也证明了这一点 现代VM技术比模式匹配更有效。 由于这两个原因,我认为模式有一定的作用 匹配。强>
编辑:我认为这需要一些更好的解释和一个例子。访问者模式通常用于访问树或类似的每个节点,例如抽象语法树(AST)。使用优秀Scalariform中的示例。 Scalariform通过解析Scala然后遍历AST并将其写出来格式化scala代码。其中一个提供的方法采用AST并按顺序创建所有令牌的简单列表。用于此的方法是:
private def immediateAstNodes(n: Any): List[AstNode] = n match {
case a: AstNode ⇒ List(a)
case t: Token ⇒ Nil
case Some(x) ⇒ immediateAstNodes(x)
case xs @ (_ :: _) ⇒ xs flatMap { immediateAstNodes(_) }
case Left(x) ⇒ immediateAstNodes(x)
case Right(x) ⇒ immediateAstNodes(x)
case (l, r) ⇒ immediateAstNodes(l) ++ immediateAstNodes(r)
case (x, y, z) ⇒ immediateAstNodes(x) ++ immediateAstNodes(y) ++ immediateAstNodes(z)
case true | false | Nil | None ⇒ Nil
}
def immediateChildren: List[AstNode] = productIterator.toList flatten immediateAstNodes
这是一项可以通过Java中的访问者模式完成的工作,但更简洁地通过Scala中的模式匹配来完成。在Scalastyle(Checkstyle for Scala)中,我们使用此方法的修改形式,但有一个微妙的变化。我们需要遍历树,但每个检查只关心某些节点。例如,对于EqualsHashCodeChecker,它只关心定义的equals和hashCode方法。我们使用以下方法:
protected[scalariform] def visit[T](ast: Any, visitfn: (Any) => List[T]): List[T] = ast match {
case a: AstNode => visitfn(a.immediateChildren)
case t: Token => List()
case Some(x) => visitfn(x)
case xs @ (_ :: _) => xs flatMap { visitfn(_) }
case Left(x) => visitfn(x)
case Right(x) => visitfn(x)
case (l, r) => visitfn(l) ::: visitfn(r)
case (x, y, z) => visitfn(x) ::: visitfn(y) ::: visitfn(z)
case true | false | Nil | None => List()
}
请注意,我们以递归方式调用visitfn()
,而不是visit()
。这允许我们重用此方法来遍历树而无需复制代码。在我们的EqualsHashCodeChecker
中,我们有:
private def localvisit(ast: Any): ListType = ast match {
case t: TmplDef => List(TmplClazz(Some(t.name.getText), Some(t.name.startIndex), localvisit(t.templateBodyOption)))
case t: FunDefOrDcl => List(FunDefOrDclClazz(method(t), Some(t.nameToken.startIndex), localvisit(t.localDef)))
case t: Any => visit(t, localvisit)
}
所以这里唯一的样板是模式匹配的最后一行。在Java中,上面的代码可以很好地实现为访问者模式,但在Scala中使用模式匹配是有意义的。另请注意,除了定义unapply()
之外,上述代码不需要修改所遍历的数据结构,如果您使用的是案例类,则会自动发生。
答案 1 :(得分:7)
布拉克埃米尔,马丁奥德斯基和约翰威廉姆斯在Matching Objects with Patterns的论文中对这个问题进行了很好的调查
答案 2 :(得分:2)
最近(2019年), Scala 世界中的访客模式引起了很多关注。
请参阅此处的示例2关于该主题的博客:
Travis Brown 的https://meta.plasm.us/posts/2019/09/23/scala-and-the-visitor-pattern/
https://medium.com/@supermanue/gof-design-patterns-in-scala-visitor-f3b8c91e0488,由 ManuelRodríguez