Подтвердить что ты не робот

Шаблон посетителя в Scala

Есть ли какие-либо варианты использования Шаблон посетителей в Scala?

Должен ли я использовать Match Matching в Scala каждый раз, когда я использовал шаблон посетителя в Java?

4b9b3361

Ответ 1

Да, вы должны начать с сопоставления с образцом вместо шаблона посетителя. См. Интервью с Мартином Одерским (мой акцент):

Итак, правильный инструмент для работы действительно зависит от того, в каком направлении вы хочу расширить. Если вы хотите расширить с помощью новых данных, классический объектно-ориентированный подход с виртуальными методами. Если ты хочешь чтобы данные фиксировались и расширялись с новыми операциями, затем шаблоны гораздо лучше подходят. На самом деле шаблон дизайна - не путают с сопоставлением с образцом в объектно-ориентированном программировании, называемом шаблон посетителя, который может представлять некоторые из вещей, которые мы делаем с сопоставление шаблонов объектно-ориентированным способом на основе виртуального метода отправка. Но в практическом использовании шаблон посетителя очень громоздкий. Вы не могут делать многие вещи, которые очень легко сочетаются с сопоставлением с образцом. У вас очень тяжелые гости. И также оказывается, что с современная технология 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.