2010-09-12 44 views
0

我正在尋找從通用框架調用特定接口的好替代方案。我將用代碼來舉例說明。請看問題部分,示例代碼主要是爲了徹底性,並將該示例放入真實場景中。類型安全實現的Java通用接口

示例

假設我們要基於組件列表構建報告。假設我們有兩個特定組件類型:

public interface Component { ... } 
public class PDFComponents extends Component { ... } 
public class WordComponents extends Component { ... } 

每個組件都有一個報表製作工具實現,例如,

public interface ReportBuilder { ... } 
public class PDFReportBuilder extends ReportBuilder { ... } 
public class WordReportBuilder extends ReportBuilder { ... } 

它建立特定的報表實現

public interface Report { ... } 
public class PDFReport extends ReportBuilder { ... } 
public class WordReport extends ReportBuilder { ... } 

最後我們有定位服務組件並從組件生成報告。

public class ReportService { 
    ReportComponentRepository repo; 
    List<ReportBuilder> builders; 

    public <T extends Report> T getReport(Class<T> reportType) { 
     // Get report components. E.g., this might return List<PDFComponent> 
     List<Component> reportComponents = repo.getReportComponents(id); 

     // Build report from components using one of the registered builders 
     for (ReportBuilder builder : builders) { 
      if (builder.buildFor(reportType) { 
       return builder.buildReport(report); 
      } 
     } 
    } 
} 

例如,使用服務

List<PDFReport> reports = new ReportService().getReport(PDFReport.class); 

問題

我們的問題。我如何設計一個通用的ReportBuilder接口,允許類型安全實現?

例如,選擇界面:

public Report buildReport(List<? extends Component> components); 

會導致醜陋,在它的實現:

public class PDFReportBuilder implements ReportBuilder { 

    @Override 
    public Report buildReport(List<? extends Component> components) { 
     PDFReport report; 

     for (Component component : components) { 
      if (component instanceOf PDFComponent) { 
       // assemble report ... 
       report.includeComponent(component); 
      } 
     } 

     return report; 
    } 
} 

當我們真正想要的界面爲PDFReportBuilder是如

public Report buildReport(List<PDFComponent> component) { ... } 
+0

要實現的接口,而不是延長他們的,我想簡化的界面,'PDFReport'實現'Report',和類/接口名稱之後的括號不是真的存在。 – 2010-09-12 19:47:01

+0

你是對的。從我的文章來看,我發現我對一些細節很粗心。現在已經糾正了。 – 2010-09-12 19:51:17

回答

2

它適用於將組件的類型轉換爲ReportBuilder的類型變量:

public interface ReportBuilder<T extends Component> { 
    public Report buildReport(List<T> components); 
} 

public class PDFReportBuilder implements ReportBuilder<PDFComponent> { 
    public Report buildReport(List<PDFComponent> components); 
} 

您必須評估您是否確實想要ReportBuilder中的類型變量。這並不總是正確的選擇。此外,如果您還希望PDFReportBuilder.buildReport的返回類型爲PDFReport,那麼您還需要將其作爲類型變量(即public interface ReportBuilder<T extends Component, S extends Report>)。

+0

現貨。有時我會在應用一些更高級的技術時忘記泛型的基礎知識。我同意當參數T和返回類型S都被指定時它是最強大的。 – 2010-09-13 08:58:00

+0

您對於返回類型的陳述不正確。自Java 1.5以來,重寫(或實現)方法被允許指定更具體的返回類型。 – ILMTitan 2010-09-13 15:07:02

2

在我看來,你似乎設置了一個凌亂的實現,有三個並行繼承層次結構。請問,爲什麼你不能合併Component和ReportBuilder的共享行爲?實際上,通過強制服務調用者知道他們想要的報告的子類,您會失去對組件的任何抽象。

我建議通過減少或消除參數buildReport()

public class ReportService { 
    ReportComponentRepository repo; 
    List<ReportBuilder> builders; 

    public <T extends Report> T getReport(Class<T> reportType) { 

     // Build report from components using one of the registered builders 
     for (ReportBuilder builder : builders) { 
      if (builder.buildFor(reportType) { 
       //don't pass components - if there's a requirement 
       //for a strongly typed subclass of Component, just 
       //let the Report instance figure it out. 
       return builder.buildReport(); 
      } 
     } 
    } 
} 


//example use 
public class PDFReportBuilder implements ReportBuilder { 

    ComponentSource componentSource; 

    @Override 
    public Report buildReport() { 
     PDFReport report; 

     for (PDFComponent component : componentSource.getPDFComponents()) { 
      // assemble report ... 
      report.includeComponent(component); 
      // no instanceof operations! 
     } 

     return report; 
    } 
} 
+0

這是一個絕妙的想法,這絕對是一種減少應用程序邏輯複雜度的好方法。 – 2010-09-13 08:52:32