加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

设计模式 – Scala中的访客模式

发布时间:2020-12-16 09:43:32 所属栏目:安全 来源:网络整理
导读:有没有任何用例在Scala中使用 Visitor Pattern? 每次在Java中使用访客模式时,我应该在Scala中使用Pattern Matching吗? 解决方法 是的,您应该可以从模式匹配开始,而不是访问者模式。看这个 interview with Martin Odersky(我的重点): So the right tool
有没有任何用例在Scala中使用 Visitor Pattern?

每次在Java中使用访客模式时,我应该在Scala中使用Pattern Matching吗?

解决方法

是的,您应该可以从模式匹配开始,而不是访问者模式。看这个 interview with Martin Odersky(我的重点):

So the right tool for the job really depends on which direction you
want to extend. If you want to extend with new data,you pick the
classical object-oriented approach with virtual methods. If you want
to keep the data fixed and extend with new operations,then patterns
are a much better fit. There’s actually a design pattern—not to be
confused with pattern matching—in object-oriented programming called
the visitor pattern,which can represent some of the things we do with
pattern matching in an object-oriented way,based on virtual method
dispatch. But in practical use the visitor pattern is very bulky. You
can’t do many of the things that are very easy with pattern matching.
You end up with very heavy visitors. And it also turns out that with
modern VM technology it’s way more innefficient than pattern matching.
For both of these reasons,I think there’s a definite role for pattern
matching.

编辑:我认为这需要一个更好的解释和一个例子。访问者模式通常用于访问树或类似的每个节点,例如抽象语法树(AST)。使用优秀的Scalariform.的例子Scalariform格式scala代码通过解析Scala,然后遍历AST,写出来。提供的方法之一采用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(Scala的Checkstyle)中,我们使用这种方法的修改形式,但是有一个微妙的变化。我们需要遍历树,但每个检查只关心某些节点。例如,对于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,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()之外,上述代码不需要对正在遍历的数据结构进行修改,如果您正在使用案例类,则会自动执行。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读