2011-12-23 2 views

답변

41

예. 방문자 패턴 대신 패턴 일치로 시작해야합니다. 이 interview with Martin Odersky (내 강조)를 참조하십시오 :

그래서 작업에 적합한 도구가 정말 확장 할 하는 방향에 따라 달라집니다. 새로운 데이터로 확장하려면 가상 메소드로 고전적인 객체 지향 접근 방식 인 을 선택하십시오. 에 데이터를 고정시키고 새 작업으로 확장하려면 패턴 이 훨씬 적합합니다. 실제로 디자인 패턴이 있습니다 - 은 패턴 일치와 혼동되지 않습니다. 이라는 객체 지향 프로그래밍에서 방문자 패턴은 패턴 일치가있는 가상 메쏘드를 기반으로합니다. 발송. 그러나 실제 사용시 방문자 패턴은 매우 부피가 커집니다. 은 패턴 일치가 매우 쉬운 많은 작업을 수행 할 수 없습니다. 당신은 매우 무거운 방문자로 끝납니다. 또한 현대의 VM 기술 을 사용하면 패턴 일치보다 비효율적입니다. 이러한 두 가지 이유 때문에 패턴 과 일치하는 역할이 분명하다고 생각합니다.

편집 : 이것은 좀 더 나은 설명과 예제가 필요하다고 생각합니다. 방문자 패턴은 종종 AST (Abstract Syntax Tree)와 같이 트리 또는 유사 사이트의 모든 노드를 방문하는 데 사용됩니다. 우수한 Scalariform의 예를 사용하십시오. Scalariform은 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 

이 잘 자바 방문자 패턴으로 수행 할 수 있지만, 훨씬 더 간결 스칼라에서 패턴 매칭 수행 할 수있는 작업입니다. 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, y, z)     => visitfn(x) ::: visitfn(y) ::: visitfn(z) 
    case true | false | Nil | None => List() 
} 

공지 사항 우리는 반복적으로, visit()visitfn()을하지 호출하고 있습니다. 이렇게하면 코드를 복제하지 않고이 메소드를 재사용하여 트리를 탐색 할 수 있습니다. 우리 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()을 정의하는 것 외에 이동중인 데이터 구조를 수정할 필요가 없습니다.

+0

니스 호출 할 수 있습니다. 이것은 특정 '액션'코드에서 방문 기능을 분리하여 스칼라에서 방문자 패턴을 찾을 수 있었던 최고의 예제 코드입니다. – Core

+0

"방문자 패턴은 종종 나무 나 그와 유사한 노드의 모든 노드를 방문하는 데 사용됩니다."- IMHO 이는 일반적인 오해입니다. 방문자 패턴은 이름에도 불구하고 여러 노드를 방문하는 것과 아무런 관련이 없습니다. GoF의 원래 의도는 컴파일러가 선언 된 모든 하위 유형을 정확하게 처리하도록 요구할 때 엄격한 ADT 의미를 갖는 것입니다. 이것은 부속 유형의 닫힌 세트를 지정하며 비공식 부속 유형의 추가를 허용하지 않습니다. – beefeather

+0

수백 개의 노드 유형이 있고 각기 다른 코드로 방문해야 할 때 어떻게됩니까? –