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