2009-05-15 27 views
10

Tôi có một tình huống thú vị và tôi tự hỏi nếu có một cách tốt hơn để làm điều này. Tình hình là điều này, tôi có một cấu trúc cây (một cây cú pháp trừu tượng, cụ thể) và một số nút có thể chứa các nút con của các loại khác nhau nhưng tất cả được mở rộng từ một lớp cơ sở nhất định.Loại an toàn, Java Generics và truy vấn

Tôi muốn thường xuyên thực hiện truy vấn trên cây này và tôi muốn lấy lại các kiểu con cụ thể mà tôi quan tâm. Vì vậy, tôi đã tạo một lớp biến vị ngữ mà sau đó tôi có thể chuyển vào một phương thức truy vấn chung. Lúc đầu, tôi đã có một phương pháp truy vấn trông giống như sau:

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

đối số Class được sử dụng chỉ để chỉ loại trả về. Điều làm tôi băn khoăn về cách tiếp cận này là tất cả các biến vị ngữ của tôi đã sẵn sàng cho các kiểu cụ thể để có thông tin dư thừa ở đây. Một cuộc gọi thông thường có thể trông giống như:

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

Vì vậy, tôi refactored nó như thế này:

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

Trường hợp giao diện IElementPredicate trông như thế này:

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

Vấn đề ở đây là vị giao diện được mở rộng để cung cấp đối tượng Class thay thế. Nó làm cho phương pháp viết thực tế hơn một chút và nó bổ sung thêm một chút công việc bằng cách viết biến vị ngữ, nhưng cả hai đều là những thứ "một lần" cơ bản nhỏ và nó làm cho cuộc gọi truy vấn đẹp hơn nhiều bởi vì bạn không phải thêm đối số thừa (có thể dư thừa), ví dụ

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

Tôi chưa nhận thấy mẫu này trước đây. Đây có phải là cách điển hình để đối phó với ngữ nghĩa của các Generics Java không? Chỉ cần tò mò nếu tôi thiếu một số mô hình tốt hơn.

Cam kết?

CẬP NHẬT:

Một câu hỏi là bạn cần lớp học để làm gì? Đây là việc thực hiện findAll:

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; 
} 

Mặc dù đối sánh đúng với T, tôi cần đảm bảo đối tượng là T trước khi tôi có thể gọi nó. Đối với điều đó, tôi cần các phương thức "isInstance" và "cast" của Class (theo như tôi có thể nói).

Trả lời

1

Mẫu gần nhất "Tôi nghĩ là mã thông báo loại và Hướng dẫn tạo đề xuất chúng. Bạn cũng có thể biến vị từ cơ sở thành super-type-token (tiện ích Gafter a.k.a.) và lưu thêm một vài dòng khi định nghĩa các biến vị ngữ mới.

1

Nếu muốn, bạn có thể sử dụng mẫu khách truy cập hoặc các biến thể để khám phá và xây dựng rõ ràng. Xem the hibernate wiki page on this. Tôi đã suy nghĩ về nếu bạn sẽ giải quyết vấn đề của bạn hoàn toàn xem xét các đặc điểm của loại tẩy xoá, và tôi không hoàn toàn chắc chắn nó sẽ làm việc tất cả các cách.

chỉnh sửa: Tôi đã thấy phần bổ sung của bạn. Nếu bạn sẽ làm cho hệ thống phân cấp vị ngữ của bạn tuân theo hệ thống phân cấp khách truy cập, bạn sẽ không cần phải thực hiện trước cuộc gọi kết hợp. Giống như vậy (chưa được kiểm tra):

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

Tôi thường xuyên sử dụng khách truy cập (trong dự án này rất giống nhau) nhưng trong trường hợp cụ thể này, mẫu khách truy cập sẽ thêm nhiều mã và độ phức tạp hơn tôi nghĩ. –

+0

Tôi đồng ý với bạn, nó là một loại biến thể chuyển đổi theo cách nó có tác dụng phụ lớn. Thật khó để đảo ngược chủ nghĩa chung như bạn muốn. Tôi tin rằng giải pháp của bạn là tốt nhất, bởi vì không có khách truy cập, tôi tin rằng bạn sẽ cần phải ràng buộc trễ hoặc kiểm tra loại rõ ràng (theo cách thức bằng() hoạt động). Giải pháp của bạn tốt hơn vì nó tập trung vào việc kiểm tra kiểu thay vì đặt gánh nặng đó lên những người triển khai. Bạn cũng thấy vấn đề này trong các triển khai Comparable chung. –

+0

Và với sự ràng buộc trễ tôi có nghĩa là Java nên thực hiện độ phân giải phương thức quá tải dựa trên loại đối tượng, không phải trên kiểu tham chiếu. Đây là cơ bản những gì bạn thực hiện trong mã của bạn. –

1

Cấu trúc cây của bạn rất giống với đối tượng XML DOM. Bạn đã xem xét dịch cây của mình thành cấu trúc DOM và sử dụng XPath để thực hiện truy vấn của mình chưa? Nó có thể ít hơn rất nhiều mã tùy chỉnh.

0

Tôi nghĩ rằng đó là cách tốt đẹp và sạch sẽ để làm điều đó và tôi có thể sẽ làm điều đó theo cùng một cách.