Есть ли какие-либо варианты использования Шаблон посетителей в Scala?
Должен ли я использовать Match Matching в Scala каждый раз, когда я использовал шаблон посетителя в Java?
Есть ли какие-либо варианты использования Шаблон посетителей в Scala?
Должен ли я использовать Match Matching в Scala каждый раз, когда я использовал шаблон посетителя в Java?
Да, вы должны начать с сопоставления с образцом вместо шаблона посетителя. См. Интервью с Мартином Одерским (мой акцент):
Итак, правильный инструмент для работы действительно зависит от того, в каком направлении вы хочу расширить. Если вы хотите расширить с помощью новых данных, классический объектно-ориентированный подход с виртуальными методами. Если ты хочешь чтобы данные фиксировались и расширялись с новыми операциями, затем шаблоны гораздо лучше подходят. На самом деле шаблон дизайна - не путают с сопоставлением с образцом в объектно-ориентированном программировании, называемом шаблон посетителя, который может представлять некоторые из вещей, которые мы делаем с сопоставление шаблонов объектно-ориентированным способом на основе виртуального метода отправка. Но в практическом использовании шаблон посетителя очень громоздкий. Вы не могут делать многие вещи, которые очень легко сочетаются с сопоставлением с образцом. У вас очень тяжелые гости. И также оказывается, что с современная технология VM она более эффективна, чем сопоставление моделей. По обоим причинам я считаю, что для соответствия.
EDIT: Я думаю, что это требует немного лучшего объяснения и примера. Шаблон посетителя часто используется для посещения каждого node в дереве или подобном, например, абстрактном синтаксическом дереве (AST). Используя пример из превосходного 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 (Checkstyle для Scala) мы используем модифицированную форму этого метода, но с тонким изменением. Нам нужно пересечь дерево, но каждая проверка касается только определенных узлов. Например, для EqualsHashCodeChecker он заботится только о методах равных и хэш-кодов. Мы используем следующий метод:
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()
, которое происходит автоматически, если вы используете классы case.
Существует хороший обзор этого вопроса в документе Соответствие объектов с шаблонами Бураком Эмиром, Мартином Одерским и Джоном Уильямсом