题目
设计可扩展的报表导出系统
信息
- 类型:问答
- 难度:⭐⭐
考点
工厂方法模式,抽象工厂模式,设计模式选择
快速回答
使用工厂方法模式实现报表导出系统更合适:
- 定义
ReportExporter抽象类和export()抽象方法 - 为每种格式(PDF/Excel/CSV)创建具体子类
- 通过
ExporterFactory接口和具体工厂创建对象 - 客户端通过工厂获取具体导出器实例
避免使用抽象工厂模式,因无需创建产品族。
解析
问题场景
需要设计报表导出系统,支持PDF/Excel/CSV等格式,未来可能新增JSON等格式。要求系统可扩展、符合开闭原则。
原理说明
工厂方法模式:定义创建对象的接口,让子类决定实例化哪个类。适用于:
- 需要创建单一产品族
- 客户端不关心具体实现类
- 需要灵活扩展新产品
抽象工厂模式:创建相关或依赖对象的家族,不需指定具体类。适用于:
- 需要创建多个产品族(如导出器+样式生成器)
- 系统需要多套产品组合
本场景只需创建单一产品(导出器),选择工厂方法模式更合适。
代码示例
// 1. 抽象产品
public abstract class ReportExporter {
public abstract void export(ReportData data);
}
// 2. 具体产品
public class PdfExporter extends ReportExporter {
@Override
public void export(ReportData data) {
System.out.println("Exporting to PDF: " + data);
}
}
public class ExcelExporter extends ReportExporter { /* 类似实现 */ }
// 3. 工厂接口
public interface ExporterFactory {
ReportExporter createExporter();
}
// 4. 具体工厂
public class PdfExporterFactory implements ExporterFactory {
@Override
public ReportExporter createExporter() {
return new PdfExporter();
}
}
// 5. 客户端使用
public class ReportService {
public void exportReport(ReportData data, ExporterFactory factory) {
ReportExporter exporter = factory.createExporter();
exporter.export(data);
}
}
// 调用示例
ReportService service = new ReportService();
service.exportReport(data, new PdfExporterFactory());最佳实践
- 开闭原则:新增格式只需添加新工厂和导出器类,无需修改现有代码
- 依赖注入:客户端通过工厂接口依赖抽象,降低耦合
- 配置化扩展:结合Spring等框架,用配置决定具体工厂
常见错误
- 模式误用:使用抽象工厂导致过度设计(如创建不存在的产品族)
- 违反开闭原则:用if-else判断类型直接new对象
- 工厂膨胀:为每个导出器创建独立工厂,可改用简单工厂+Map缓存
扩展知识
- 简单工厂:用静态方法创建对象,但违反开闭原则
- Spring集成:通过
@Bean定义工厂,@Autowired注入具体实现 - 模式组合:工厂方法+策略模式实现导出算法动态切换
- 性能优化:工厂返回的对象可设计为无状态,复用实例