2009-05-15 4 views
10

흥미로운 상황이 있으며 더 나은 방법이 있는지 궁금합니다. 상황은 이것입니다. 트리 구조 (특히 추상 구문 트리)가 있고 일부 노드에는 다양한 유형의 자식 노드가 포함될 수 있지만 모두 주어진 기본 클래스에서 확장됩니다.유형 안전성, Java generics 및 쿼리

이 트리에서 자주 쿼리를 수행하고 싶습니다. 관심이있는 특정 하위 유형을 되돌리고 싶습니다. 따라서 일반 쿼리 방법으로 전달할 수있는 조건부 클래스를 만들었습니다.

public <T extends Element> List<T> findAll(IElementPredicate pred, Class<T> c); 

Class 인수가 바로 반환 형식을 나타 내기 위해 사용되었다 : 처음에 나는이처럼 보였다 쿼리 방법을했다. 이 접근 방법에 대해 저를 괴롭혔던 것은 모든 술어가 이미 특정 유형에 대한 것이므로 여기에 중복 정보가 있다는 것입니다.

List<Declaration> decls = 
    scope.findAll(new DeclarationPredicate(), Declaration.class); 

그래서 나는이처럼 리팩토링 :

public <T extends Element> List<T> findAll(IElementPredicate<T> pred); 

IElementPredicate 인터페이스는 다음과 같다 여기서처럼 전형적인 전화가 보일 수 있습니다 요점은 여기 술어 것을

public interface IElementPredicate<T extends Element> { 
    public boolean match(T e); 
    public String getDescription(); 
    public Class<T> getGenericClass(); 
} 

입니다

인터페이스가 확장되어 Class 개체를 대신 제공합니다. 실제 findAll 메소드를 작성하는 데 약간의 작업이 필요하며, 술어 작성에 조금 더 많은 작업이 추가되지만, 본질적으로 작은 "일회성"일 뿐이며 쿼리 호출을 훨씬 더 멋지게 만듭니다. 여분의 (잠재적으로 여분의) 인수를 추가해야합니다.

List<Declaration> decls = scope.findAll(new DeclarationPredicate()); 

이전에이 패턴을 발견하지 못했습니다. 이것은 Java generics의 의미를 다루는 일반적인 방법인가요? 단지 내가 더 좋은 패턴을 놓치고 있는지 궁금해.

수수료가 필요하십니까?

업데이트 :

하나의 질문으로 클래스가 필요합니까? 그것은 일치하는 만 T 걸리는 사실이지만, 나는 그것을 호출하기 전에 개체가 T 있는지 확인해야합니다

public <T extends Element> List<T> findAll(IElementPredicate<T> pred) { 
    List<T> ret = new LinkedList<T>(); 
    Class<T> c = pred.getGenericClass(); 
    for(Element e: elements) { 
     if (!c.isInstance(e)) continue; 
     T obj = c.cast(e); 
     if (pred.match(obj)) { 
      ret.add(c.cast(e)); 
     } 
    } 
    return ret; 
} 

: 여기 findall은의 구현입니다. 이를 위해서는 클래스의 "isInstance"및 "cast"메서드가 필요합니다 (가능한 한 멀리 말할 수 있음).

답변

1

가장 가까운 "패턴"은 토큰 종류 인이며 제네릭 가이드에서 권장합니다. 또한 기본 술어를 super-type-token (Gafter 가젯)으로 바꿀 수 있으며 새 술어를 정의 할 때 여분의 몇 줄을 저장할 수 있습니다.

1

원하는 경우 방문객 패턴 또는 변형을 사용하여 명시 적 생성 및 전송을 피할 수 있습니다. the hibernate wiki page on this을 참조하십시오. 나는 유형 소거의 특성을 완전히 고려하여 문제를 해결할 수 있을지 생각하고 있었고, 그것이 완전히 작동 할 것이라는 것을 완전히 확신하지는 못했습니다.

편집 : 귀하의 추가 사항을 보았습니다. 프리디 케이트 계층 구조를 방문객 계층 구조로 만들려면 경기 호출 전에 캐스트가 필요하지 않습니다. 이렇게 (테스트되지 않음) :

interface Element { 
    public boolean accept(ElementPredicateVisitor v); 
} 

class Declaration implements Element { 
    public boolean accept(ElementPredicateVisitor v) { 
     return v.visit(this); 
    }  
} 

class TaxReturn implements Element { 
    public boolean accept(ElementPredicateVisitor v) { 
     return v.visit(this); 
    }  
} 


interface IElementPredicate { 
    public void match(Element e); 
} 

class ElementPredicateVisitor implements IElementPredicate { 
    public boolean match(Element e) { 
     return e.accept(this); 
    } 
    /** 
    * default values 
    */ 
    boolean visit(Declaration d) { return false; } 
    boolean visit(TaxReturn tr) { return false; } 
} 

class DeclarationNamePredicate extends ElementPredicateVisitor { 
    boolean visit(Declaration d) { 
     return d.dSpecificExtraName() == "something" 
    } 
} 

class TaxReturnSumPredicate extends ElementPredicateVisitor { 
    boolean visit(TaxReturn tr) { 
     return tr.sum() > 1000; 
    } 
} 


public <T extends Element> List<T> findAll(IElementPredicate pred) { 
    List<T> ret = new LinkedList<T>(); 
    for(Element e: elements) {    
     if (pred.match(obj)) { 
       ret.add((T) obj); 
     } 
    } 
    return ret; 
} 
+0

방문자를 빈번히 (실제로이 프로젝트에서) 사용하지만,이 특별한 경우 방문자 패턴은 필 요하다고 생각되는 것보다 훨씬 많은 코드와 복잡성을 추가합니다. –

+0

나는 당신과 동의한다, 그것은 부작용이 큰 부작용을 일으키는 방식으로 전환된다. 당신이 원하는 것처럼 일반성을 뒤집기가 어렵습니다. 방문자가 없다면 늦은 바인딩이나 명시 적 형식 검사 (equals()가 작동하는 방식)가 필요하기 때문에 솔루션이 최고라고 믿습니다. 당신의 솔루션은 구현 자에게 부담을주는 대신 유형 검사를 중앙 집중화하므로 더 좋습니다. 이 문제는 일반적인 Comparable 구현에서도 볼 수 있습니다. –

+0

그리고 늦은 바인딩을 통해 나는 Java가 참조 유형이 아닌 객체 유형에 기반한 오버로드 된 메소드 해석을 구현해야한다는 것을 의미했습니다. 이것은 기본적으로 코드에서 구현 한 것입니다. –

1

트리 구조는 XML DOM 객체와 매우 유사하게 들립니다. 트리를 DOM 구조로 변환하고 XPath를 사용하여 쿼리를 수행하는 것을 고려 했습니까? 사용자 정의 코드가 훨씬 적을 수 있습니다.

0

나는 그것이 멋지고 깨끗한 방법이라고 생각하며 나는 같은 방식으로 그것을 할 것입니다.