Шаблон посетителей в Scala - PullRequest
25 голосов
/ 23 декабря 2011

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

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

Ответы [ 2 ]

45 голосов
/ 23 декабря 2011

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

Так что правильный инструмент для работы действительно зависит от того, в каком направлении вы хочу продлить. Если вы хотите расширить с новыми данными, вы выбираете классический объектно-ориентированный подход с виртуальными методами. Если ты хочешь сохранять данные фиксированными и расширяться новыми операциями, а затем шаблонами гораздо лучше подходят. Там на самом деле шаблон проектирования - не быть путать с сопоставлением с образцом - в объектно-ориентированном программировании называется шаблон посетителя, который может представлять некоторые вещи, которые мы делаем с сопоставление с образцом объектно-ориентированным способом на основе виртуального метода отправка. Но при практическом использовании структура посетителей очень громоздкая. Вы не может сделать много вещей, которые очень легко с сопоставлением с образцом. Вы заканчиваете с очень тяжелыми посетителями. И также оказывается, что с современные технологии виртуальных машин гораздо более неэффективны, чем сопоставление с образцом. По обеим этим причинам, я думаю, что есть определенная роль для модели согласование.

РЕДАКТИРОВАТЬ: Я думаю, что это требует немного лучшего объяснения и примера. Шаблон посетителя часто используется для посещения каждого узла в дереве или аналогичном, например, абстрактном синтаксическом дереве (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 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(), что происходит автоматически, если вы используете классы case.

7 голосов
/ 23 декабря 2011

Хороший обзор этого вопроса приведен в статье Сопоставление объектов с узорами Бурака Эмира, Мартина Одерского и Джона Уильямса

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...