设计模式详解—建造者模式
设计模式详解:建造者模式
一、模式概述
建造者模式(Builder Pattern)是创建型设计模式中最具工程实践价值的模式之一,其核心意图在于将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。这一模式直面软件开发中一个普遍痛点:当对象的构造参数众多、存在多种配置组合、或构建步骤存在严格的先后依赖时,传统的构造函数或工厂方法往往导致代码臃肿、可读性崩塌、维护成本激增。
建造者模式的精髓在于"分步构建"与"延迟绑定"。它将对象的创建拆解为一系列有序的步骤,由指导者(Director)控制构建流程,由具体的建造者(Concrete Builder)实现各步骤的细节,最终由客户端获取完整产品。这种解耦使得构建算法可以独立于具体的组成部分及其装配方式而演化。
二、模式结构
建造者模式包含四个核心角色,形成清晰的职责分层:
产品(Product):被构建的复杂对象,通常包含多个组成部分。
抽象建造者(Builder):声明创建产品各个部件的抽象接口,以及返回最终产品的方法。
具体建造者(Concrete Builder):实现抽象建造者接口,定义并跟踪其创建的具体表示,提供获取最终产品的方法。
指导者(Director):使用建造者接口构建产品,定义构建步骤的执行顺序,隔离客户端与具体构建过程。
客户端(Client):创建具体建造者对象,将其传递给指导者,或直接使用建造者进行链式调用。
三、深度案例:企业级报表引擎构建
以下展示一个真实场景下的建造者模式应用——支持多格式、多数据源、复杂布局的企业级报表生成系统。
3.1 产品层:报表对象模型
java
public class Report {
private String title;
private List<Section> sections = new ArrayList<>();
private Header header;
private Footer footer;
private PageConfig pageConfig;
private DataSource dataSource;
private ExportFormat exportFormat;
private Map<String, Object> metadata = new HashMap<>();
// 私有构造函数,强制通过建造者创建
private Report() {}
// 内部静态建造者类
public static class Builder {
private Report report = new Report();
public Builder title(String title) {
report.title = title;
return this;
}
public Builder dataSource(DataSource ds) {
report.dataSource = ds;
return this;
}
public Builder exportFormat(ExportFormat format) {
report.exportFormat = format;
return this;
}
public Builder pageSize(PageSize size) {
if (report.pageConfig == null) {
report.pageConfig = new PageConfig();
}
report.pageConfig.setSize(size);
return this;
}
public Builder pageOrientation(Orientation orientation) {
if (report.pageConfig == null) {
report.pageConfig = new PageConfig();
}
report.pageConfig.setOrientation(orientation);
return this;
}
public Builder margin(double top, double right, double bottom, double left) {
if (report.pageConfig == null) {
report.pageConfig = new PageConfig();
}
report.pageConfig.setMargins(top, right, bottom, left);
return this;
}
public Builder header(Header header) {
report.header = header;
return this;
}
public Builder footer(Footer footer) {
report.footer = footer;
return this;
}
public Builder addSection(Section section) {
report.sections.add(section);
return this;
}
public Builder metadata(String key, Object value) {
report.metadata.put(key, value);
return this;
}
// 构建前的校验与默认值填充
public Report build() {
validate();
setDefaults();
return report;
}
private void validate() {
if (report.title == null || report.title.isEmpty()) {
throw new IllegalStateException("报表标题不能为空");
}
if (report.dataSource == null) {
throw new IllegalStateException("数据源必须指定");
}
if (report.sections.isEmpty()) {
throw new IllegalStateException("至少需要一个内容区块");
}
}
private void setDefaults() {
if (report.pageConfig == null) {
report.pageConfig = PageConfig.defaultA4();
}
if (report.exportFormat == null) {
report.exportFormat = ExportFormat.PDF;
}
if (report.header == null) {
report.header = Header.defaultHeader(report.title);
}
}
}
// 业务方法
public byte[] generate() {
ReportGenerator generator = ReportGeneratorFactory
.getGenerator(exportFormat);
return generator.generate(this);
}
// Getters...
}
3.2 复杂场景:分步构建与条件装配
实际项目中,报表的构建往往涉及动态条件判断和延迟加载。以下展示带有指导者角色的完整实现:
java
// 抽象建造者:报表构建接口
public interface ReportBuilder {
void reset();
void buildTitle(String title);
void buildDataSource(Connection conn, String query);
void buildDataSource(JpaRepository<?, ?> repository, Specification<?> spec);
void buildPageConfig(PageConfig config);
void buildHeader(HeaderTemplate template);
void buildFooter(FooterTemplate template);
void buildSection(SectionTemplate template, DataBinding binding);
void buildChart(ChartConfig chartConfig, DataSeries series);
void buildCrossTab(CrossTabDefinition definition);
void setExportFormat(ExportFormat format);
void setMetadata(Map<String, Object> metadata);
Report getResult();
}
// 具体建造者:PDF报表建造者
public class PdfReportBuilder implements ReportBuilder {
private Report.Builder builder;
private List<Section> pendingSections = new ArrayList<>();
private boolean titleSet = false;
@Override
public void reset() {
this.builder = new Report.Builder();
this.pendingSections.clear();
this.titleSet = false;
}
@Override
public void buildTitle(String title) {
builder.title(title);
this.titleSet = true;
}
@Override
public void buildDataSource(Connection conn, String query) {
validateState("数据源必须在标题之后设置", titleSet);
SqlDataSource ds = new SqlDataSource(conn, query);
builder.dataSource(ds);
}
@Override
public void buildDataSource(JpaRepository<?, ?> repository, Specification<?> spec) {
validateState("数据源必须在标题之后设置", titleSet);
JpaDataSource ds = new JpaDataSource(repository, spec);
builder.dataSource(ds);
}
@Override
public void buildPageConfig(PageConfig config) {
builder.pageSize(config.getSize())
.pageOrientation(config.getOrientation())
.margin(config.getTopMargin(), config.getRightMargin(),
config.getBottomMargin(), config.getLeftMargin());
}
@Override
public void buildHeader(HeaderTemplate template) {
Header header = template.renderForPdf();
builder.header(header);
}
@Override
public void buildFooter(FooterTemplate template) {
Footer footer = template.renderForPdf();
builder.footer(footer);
}
@Override
public void buildSection(SectionTemplate template, DataBinding binding) {
Section section = template.render(binding);
// PDF特有处理:分页符优化、字体嵌入
section.optimizeForPagination();
section.embedFonts();
pendingSections.add(section);
}
@Override
public void buildChart(ChartConfig chartConfig, DataSeries series) {
// PDF中图表转为矢量图形
ChartRenderer renderer = new VectorChartRenderer();
Image chartImage = renderer.render(chartConfig, series);
Section chartSection = new ImageSection(chartImage);
pendingSections.add(chartSection);
}
@Override
public void buildCrossTab(CrossTabDefinition definition) {
// PDF交叉表需要预计算列宽
CrossTabCalculator calculator = new CrossTabCalculator(definition);
Table table = calculator.calculateForWidth(792); // A4宽度点数
pendingSections.add(new TableSection(table));
}
@Override
public void setExportFormat(ExportFormat format) {
if (format != ExportFormat.PDF) {
throw new IllegalArgumentException("PDF建造者仅支持PDF格式");
}
builder.exportFormat(format);
}
@Override
public void setMetadata(Map<String, Object> metadata) {
metadata.forEach(builder::metadata);
}
@Override
public Report getResult() {
// 延迟装配所有区块
pendingSections.forEach(builder::addSection);
return builder.build();
}
private void validateState(String message, boolean condition) {
if (!condition) {
throw new IllegalStateException(message);
}
}
}
// 具体建造者:Excel报表建造者
public class ExcelReportBuilder implements ReportBuilder {
private Report.Builder builder;
private Workbook workbook;
private Sheet currentSheet;
private int currentRow = 0;
@Override
public void reset() {
this.builder = new Report.Builder();
this.workbook = new XSSFWorkbook();
this.currentSheet = workbook.createSheet();
this.currentRow = 0;
}
@Override
public void buildTitle(String title) {
builder.title(title);
// Excel特有:创建标题行并设置样式
Row titleRow = currentSheet.createRow(currentRow++);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue(title);
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 16);
style.setFont(font);
titleCell.setCellStyle(style);
// 合并标题单元格
currentSheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 10));
}
@Override
public void buildDataSource(Connection conn, String query) {
builder.dataSource(new SqlDataSource(conn, query));
}
@Override
public void buildDataSource(JpaRepository<?, ?> repository, Specification<?> spec) {
builder.dataSource(new JpaDataSource(repository, spec));
}
@Override
public void buildPageConfig(PageConfig config) {
// Excel忽略页面配置,但记录打印区域
builder.pageSize(PageSize.A4); // 仅作标记
}
@Override
public void buildHeader(HeaderTemplate template) {
// Excel页眉在打印时生效
Header header = template.renderForExcel();
builder.header(header);
}
@Override
public void buildFooter(FooterTemplate template) {
Footer footer = template.renderForExcel();
builder.footer(footer);
}
@Override
public void buildSection(SectionTemplate template, DataBinding binding) {
Section section = template.render(binding);
// Excel特有处理:直接写入单元格而非保存区块对象
if (section instanceof TableSection) {
writeTableToSheet((TableSection) section);
}
}
@Override
public void buildChart(ChartConfig chartConfig, DataSeries series) {
// Excel内嵌图表对象
Drawing<?> drawing = currentSheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, currentRow, 10, currentRow + 20);
Chart chart = drawing.createChart(anchor);
// 配置图表数据...
currentRow += 22;
}
@Override
public void buildCrossTab(CrossTabDefinition definition) {
CrossTabCalculator calculator = new CrossTabCalculator(definition);
Table table = calculator.calculate();
writeTableToSheet(new TableSection(table));
}
@Override
public void setExportFormat(ExportFormat format) {
if (format != ExportFormat.EXCEL) {
throw new IllegalArgumentException("Excel建造者仅支持Excel格式");
}
builder.exportFormat(format);
}
@Override
public void setMetadata(Map<String, Object> metadata) {
// Excel特有:写入文档属性
POIXMLProperties props = ((XSSFWorkbook) workbook).getProperties();
metadata.forEach((k, v) -> props.getCoreProperties().setProperty(k, String.valueOf(v)));
metadata.forEach(builder::metadata);
}
@Override
public Report getResult() {
// Excel特有:将workbook作为附件
builder.metadata("workbook", workbook);
return builder.build();
}
private void writeTableToSheet(TableSection section) {
// 具体写入逻辑...
}
}
3.3 指导者层:预定义构建流程
java
public class ReportDirector {
// 预定义:标准列表报表
public void constructStandardListReport(ReportBuilder builder,
ReportRequest request) {
builder.reset();
builder.buildTitle(request.getTitle());
builder.buildDataSource(request.getConnection(), request.getQuery());
builder.buildPageConfig(PageConfig.defaultA4());
builder.buildHeader(HeaderTemplate.standard());
builder.buildFooter(FooterTemplate.withPageNumber());
// 动态添加数据区块
ListSectionTemplate listTemplate = new ListSectionTemplate(
request.getColumns(),
request.getColumnWidths()
);
builder.buildSection(listTemplate, request.getDataBinding());
builder.setExportFormat(request.getFormat());
builder.setMetadata(request.getMetadata());
}
// 预定义:分组汇总报表
public void constructGroupSummaryReport(ReportBuilder builder,
ReportRequest request) {
builder.reset();
builder.buildTitle(request.getTitle());
builder.buildDataSource(request.getRepository(), request.getSpecification());
builder.buildPageConfig(PageConfig.landscapeA4());
builder.buildHeader(HeaderTemplate.withLogo(request.getLogo()));
builder.buildFooter(FooterTemplate.withPageNumberAndDate());
// 分组头
for (GroupDefinition group : request.getGroups()) {
GroupHeaderTemplate groupHeader = new GroupHeaderTemplate(group);
builder.buildSection(groupHeader, request.getDataBinding());
}
// 明细数据
DetailSectionTemplate detailTemplate = new DetailSectionTemplate(
request.getDetailColumns()
);
builder.buildSection(detailTemplate, request.getDataBinding());
// 汇总图表
if (request.isIncludeChart()) {
ChartConfig chartConfig = new ChartConfig(ChartType.BAR, "销售额趋势");
DataSeries series = request.getSummarySeries();
builder.buildChart(chartConfig, series);
}
// 交叉表
if (request.isIncludeCrossTab()) {
CrossTabDefinition crossTab = request.getCrossTabDefinition();
builder.buildCrossTab(crossTab);
}
builder.setExportFormat(request.getFormat());
builder.setMetadata(request.getMetadata());
}
// 预定义:仪表盘报表
public void constructDashboardReport(ReportBuilder builder,
DashboardRequest request) {
builder.reset();
builder.buildTitle(request.getTitle());
// 仪表盘通常聚合多个数据源
for (WidgetDefinition widget : request.getWidgets()) {
builder.buildDataSource(widget.getDataSource());
switch (widget.getType()) {
case CHART:
builder.buildChart(widget.getChartConfig(), widget.getSeries());
break;
case KPI:
builder.buildSection(new KpiSectionTemplate(widget),
widget.getBinding());
break;
case TABLE:
builder.buildSection(new TableSectionTemplate(widget),
widget.getBinding());
break;
}
}
builder.setExportFormat(request.getFormat());
}
}
3.4 客户端调用
java
@Service
public class ReportService {
@Autowired
private ReportDirector director;
@Autowired
private ApplicationContext context;
public byte[] generateReport(ReportRequest request) {
// 根据请求格式获取对应建造者
ReportBuilder builder = getBuilderForFormat(request.getFormat());
// 指导者执行预定义构建流程
switch (request.getTemplateType()) {
case STANDARD_LIST:
director.constructStandardListReport(builder, request);
break;
case GROUP_SUMMARY:
director.constructGroupSummaryReport(builder, request);
break;
case DASHBOARD:
director.constructDashboardReport(builder,
(DashboardRequest) request);
break;
default:
// 自定义构建:客户端直接操作建造者
customBuild(builder, request);
}
Report report = builder.getResult();
return report.generate();
}
private ReportBuilder getBuilderForFormat(ExportFormat format) {
switch (format) {
case PDF:
return context.getBean(PdfReportBuilder.class);
case EXCEL:
return context.getBean(ExcelReportBuilder.class);
case WORD:
return context.getBean(WordReportBuilder.class);
case HTML:
return context.getBean(HtmlReportBuilder.class);
default:
throw new UnsupportedFormatException(format);
}
}
private void customBuild(ReportBuilder builder, ReportRequest request) {
builder.reset();
// 客户端完全控制构建步骤
builder.buildTitle(request.getTitle());
// ... 自定义逻辑
}
}
四、高级演进:流式API与函数式建造者
现代Java中,建造者模式常与流式API、函数式编程结合,形成更为优雅的表达:
java
// 函数式建造者接口
@FunctionalInterface
public interface ReportCustomizer {
void customize(Report.Builder builder);
}
// 服务层支持Lambda定制
public class ModernReportService {
public Report createReport(String title,
DataSource ds,
ReportCustomizer... customizers) {
Report.Builder builder = new Report.Builder()
.title(title)
.dataSource(ds);
for (ReportCustomizer customizer : customizers) {
customizer.customize(builder);
}
return builder.build();
}
}
// 客户端使用
Report report = service.createReport(
"销售分析报表",
dataSource,
b -> b.pageSize(PageSize.A3).landscape(),
b -> b.header(HeaderTemplate.withLogo(logoImage)),
b -> b.addSection(new ChartSection(salesChart)),
b -> b.addSection(new TableSection(detailTable)),
b -> b.metadata("department", "sales")
.metadata("confidentiality", "internal")
);
五、与其他模式的协作
建造者 + 工厂方法:工厂方法负责创建具体建造者实例,客户端无需硬编码建造者类型。
建造者 + 原型模式:复杂产品的初始配置可通过原型克隆获得,再由建造者进行差异化定制。
建造者 + 观察者模式:构建过程中触发进度事件,支持长耗时构建的异步反馈。
六、反模式警示
建造者模式滥用会导致"过度工程"。以下场景不宜使用:
- 对象属性少于4个且组合简单
- 所有属性均为必填且无默认值需求
- 构建步骤无顺序依赖和校验逻辑
此时,传统的构造函数或静态工厂方法更为简洁直接。
七、结语
建造者模式是应对复杂对象创建的利器,其价值不仅在于代码结构的优化,更在于将隐式的构建知识显式化、将易错的配置过程标准化。在微服务配置、云资源编排、CI/CD流水线定义等现代场景中,建造者模式的思想无处不在。掌握其精髓,意味着掌握了将复杂性拆解为可管理步骤的系统化能力。