Java通用接口与类型安全实现

我正在寻找从通用框架调用特定接口的好方法。 我将用代码举例说明。 查看问题部分 ,示例代码主要包含在内,以便彻底,并将示例放入真实场景中。

假设我们想要基于组件列表构建报告。 假设我们有两种特定的组件类型:

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

每个组件都有一个ReportBuilder实现,例如,

 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 builders; public  T getReport(Class reportType) { // Get report components. Eg, this might return List List 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 reports = new ReportService().getReport(PDFReport.class); 

现在回答这个问题。 如何设计一个通用的ReportBuilder接口,它允许类型安全的实现?

例如,选择界面:

 public Report buildReport(List components); 

在它的实现中会导致丑陋:

 public class PDFReportBuilder implements ReportBuilder { @Override public Report buildReport(List components) { PDFReport report; for (Component component : components) { if (component instanceOf PDFComponent) { // assemble report ... report.includeComponent(component); } } return report; } } 

当我们真的希望PDFReportBuilder的界面是,例如,

  public Report buildReport(List component) { ... } 

如果将Component的类型变为ReportBuilder的类型变量,它就可以工作:

 public interface ReportBuilder { public Report buildReport(List components); } public class PDFReportBuilder implements ReportBuilder { public Report buildReport(List components); } 

你必须评估你是否真的想在ReportBuilder中使用一个类型变量。 这并不总是正确的选择。 此外,如果您还希望PDFReportBuilder.buildReport具有PDFReportBuilder.buildReport的返回类型,那么您还需要将其作为类型变量(即, public interface ReportBuilder )。

在我看来,你通过拥有三个并行的inheritance层次结构来为自己设置一个混乱的实现。 请问,为什么不能合并Component和ReportBuilder的共享行为? 实际上,通过强制服务调用者知道他们想要的报告的子类,您将失去对组件的任何抽象。

我建议通过最小化或删除buildReport()的参数来简化界面

  public class ReportService { ReportComponentRepository repo; List builders; public  T getReport(Class 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; } }