[英]Java generic interfaces with typesafe implementations
我正在寻找从通用框架调用特定接口的替代方法。 我将以代码为例。 看问题部分 ,主要包括示例代码,以提高透彻性,并使示例进入实际场景。
假设我们要基于组件列表构建报告。 假设我们有两种特定的组件类型:
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<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) { ... }
如果将Component的类型设置为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>
)。
在我看来,您要通过具有三个并行继承层次结构来为混乱的实现做好准备。 请问,为什么不能合并Component和ReportBuilder的共享行为? 实际上,通过强迫服务调用者知道它们想要的Report的子类,您将失去对组件的任何抽象。
我建议通过最小化或消除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;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.