题目
设计一个支持多种文件格式导出的报表系统
信息
- 类型:问答
- 难度:⭐⭐
考点
工厂方法模式,抽象工厂模式,设计模式选择
快速回答
核心设计要点:
- 使用工厂方法模式为每种导出格式(PDF/Excel/CSV)创建独立工厂
- 通过抽象工厂模式处理格式相关的多产品族(如内容+样式)
- 关键选择依据:
- 单一产品扩展 → 工厂方法
- 多关联产品扩展 → 抽象工厂
- 客户端通过统一接口调用,与具体实现解耦
1. 问题场景分析
报表导出系统需要支持多种文件格式(如PDF/Excel/CSV),且未来可能新增格式。每种格式的导出逻辑存在显著差异:
- PDF需要处理页面布局和字体嵌入
- Excel需要处理单元格公式和样式
- CSV需处理纯文本和分隔符
2. 设计模式选择
2.1 工厂方法模式(适用场景)
当每种格式只需创建单一产品(如导出器)时:
// 产品接口
interface Exporter {
void export(ReportData data);
}
// 具体产品
class PdfExporter implements Exporter {
@Override
public void export(ReportData data) {
// PDF导出逻辑
}
}
// 工厂接口
interface ExporterFactory {
Exporter createExporter();
}
// 具体工厂
class PdfExporterFactory implements ExporterFactory {
@Override
public Exporter createExporter() {
return new PdfExporter();
}
}优势:新增格式只需添加新工厂类,符合开闭原则。
2.2 抽象工厂模式(适用场景)
当每种格式需要创建多个关联产品(如导出器+样式处理器)时:
// 抽象产品族
interface StyleProcessor {
void applyStyle();
}
interface ContentGenerator {
void generateContent();
}
// 抽象工厂
interface ExportComponentFactory {
StyleProcessor createStyleProcessor();
ContentGenerator createContentGenerator();
}
// PDF产品族实现
class PdfStyleProcessor implements StyleProcessor { /*...*/ }
class PdfContentGenerator implements ContentGenerator { /*...*/ }
class PdfComponentFactory implements ExportComponentFactory {
@Override
public StyleProcessor createStyleProcessor() {
return new PdfStyleProcessor();
}
@Override
public ContentGenerator createContentGenerator() {
return new PdfContentGenerator();
}
}优势:保证同一格式下的产品兼容性,例如Excel的样式和内容必须匹配。
3. 最佳实践
- 模式选择标准:
- 产品维度单一 → 工厂方法
- 多维度关联产品 → 抽象工厂
- 客户端解耦:
// 客户端代码 public class ReportService { private ExporterFactory exporterFactory; public ReportService(ExporterFactory factory) { this.exporterFactory = factory; // 依赖注入 } public void exportReport(ReportData data) { Exporter exporter = exporterFactory.createExporter(); exporter.export(data); } } - 扩展性:新增XML格式只需实现
XmlExporterFactory,无需修改现有代码
4. 常见错误
- 模式误用:用抽象工厂处理单一产品导致过度设计
- 工厂膨胀:为每个产品创建独立工厂(应使用工厂方法+抽象工厂组合)
- 违反DIP:客户端直接依赖具体产品类(如
new PdfExporter())
5. 扩展知识
- 与策略模式对比:
- 策略模式封装算法(如不同压缩算法)
- 工厂模式封装对象创建
- 可组合使用:工厂创建具体策略实例
- 模式变体:结合枚举简化工厂选择:
public enum ExporterType { PDF(PdfExporter::new), EXCEL(ExcelExporter::new); private final Supplier<Exporter> constructor; ExporterType(Supplier<Exporter> constructor) { this.constructor = constructor; } public Exporter create() { return constructor.get(); } }