我们在工厂实践中会遇到一种场景,那就是要构造的对象很复杂,可能包含多个组成部分,但是由这些不同的组成部分形成的对象最终的表现形式可能是不同的。此时我们就可以用到生成器模式。

生成器模式的定义是:将复杂对象的构建过程与表示分离,使得同样的构建过程可以创建不同的表示。

其核心要解决的问题就是:当一个对象包含多个组成部分,且构建过程复杂(如需要分步骤构建、存在多种配置组合)时,将构建逻辑从对象本身剥离,由专门的 “生成器” 负责,从而简化对象创建、提高灵活性。

其UML类图如下:

image.png

在上面这个图中:

  • Product:也就是产品,它代表了要被最终创建出来的那个复杂对象。
  • Builder: 是抽象生成器,它定义了构造出这个Product各个部分的接口,以及获取这个最终Product的方法。
  • ConcreteBuilder1和ConcreteBuilder2是具体的生成器:它们都实现了Builder这个抽象接口,它们负责产品Product的具体构造并保存了构件好的Product。
  • 最后的Director:它用来接受Builder对象,然后按照固定的流程调用Builder里的方法来控制构建的过程。

这样说起来还是有点抽象,我们举一个具体的例子。比如我们的开发的系统要输出报表,一个报表其内容可能是相同的,保护了报表的标题、报表内容、页眉页脚等,构建一个报表需要多个步骤,但是最终可能要输出的格式包括了Html和PDF等多种形式,因为输出的格式不同其设置的一些属性也是不同的,此时就可以使用生成器模式。
下面是具体的示例代码:

首先要创建具体的产品Product,在这里就是报表,定义创建一个报表要包含的属性和一个展示报表的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package builder;

public class Report {
private String title;
private String content;
private String footer;
private String format; // 格式:HTML/PDF等

public void setTitle(String title) {
this.title = title;
}

public void setContent(String content) {
this.content = content;
}

public void setFooter(String footer) {
this.footer = footer;
}

public void setFormat(String format) {
this.format = format;
}

// 展示报告内容
public void display() {
System.out.println("===== " + format + " 报告 =====");
System.out.println("标题: " + title);
System.out.println("内容: " + content);
System.out.println("页脚: " + footer);
System.out.println("======================");
System.out.println();
}
}

然后我们定义一个报表的生成器,它定义了如何创建一个报表Product以及如何展示这个报表

1
2
3
4
5
6
7
8
9
10
package builder;

public interface ReportBuilder {

void setTitle(String title);
void setContent(String content);
void setFooter(String footer);
Report getResult();
}

接下来分别定义Html和PDF格式报表的生成器,它们负责实现具体的报表格式的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package builder;

// 具体生成器:HTML报表
public class HtmlReportBuilder implements ReportBuilder {
private Report report;

public HtmlReportBuilder() {
this.report = new Report();
this.report.setFormat("HTML");
}

@Override
public void setTitle(String title) {
// HTML格式的标题处理
report.setTitle("<h1>" + title + "</h1>");
}

@Override
public void setContent(String content) {
// HTML格式的内容处理
report.setContent("<p>" + content + "</p>");
}

@Override
public void setFooter(String footer) {
// HTML格式的页脚处理
report.setFooter("<footer>" + footer + "</footer>");
}

@Override
public Report getResult() {
return report;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package builder;

// 具体生成器:PDF报表
public class PdfReportBuilder implements ReportBuilder {
private Report report;

public PdfReportBuilder() {
this.report = new Report();
this.report.setFormat("PDF");
}

@Override
public void setTitle(String title) {
// PDF格式的标题处理(模拟)
report.setTitle("[PDF标题] " + title);
}

@Override
public void setContent(String content) {
// PDF格式的内容处理(模拟)
report.setContent("[PDF内容] " + content);
}

@Override
public void setFooter(String footer) {
// PDF格式的页脚处理(模拟)
report.setFooter("[PDF页脚] " + footer);
}

@Override
public Report getResult() {
return report;
}
}

最后定义一个Director,它指挥 不同的生成器来生成报表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package builder;

// 指挥者:控制报表构建流程
public class ReportDirector {
private ReportBuilder builder;

public ReportDirector(ReportBuilder builder) {
this.builder = builder;
}

// 构建月度报告的固定流程
public Report buildMonthlyReport(String month, String content) {
builder.setTitle(month + "销售报告");
builder.setContent(content);
builder.setFooter("生成日期: " + java.time.LocalDate.now() + " | 机密文档");
return builder.getResult();
}

// 可以添加其他构建方法,如季度报告、年度报告等
public Report buildQuarterlyReport(String quarter, String content) {
builder.setTitle(quarter + "季度总结报告");
builder.setContent(content);
builder.setFooter("生成日期: " + java.time.LocalDate.now() + " | 内部使用");
return builder.getResult();
}
}

客户端生成不同报表的方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package builder;

public class ReportGeneratorDemo {
public static void main(String[] args) {
// 生成HTML格式的月度报告
ReportBuilder htmlBuilder = new HtmlReportBuilder();
ReportDirector director = new ReportDirector(htmlBuilder);
Report htmlReport = director.buildMonthlyReport("2024年9月", "本月销售额增长15%,达成季度目标");
htmlReport.display();

// 生成PDF格式的月度报告(仅更换生成器)
ReportBuilder pdfBuilder = new PdfReportBuilder();
director = new ReportDirector(pdfBuilder);
Report pdfReport = director.buildMonthlyReport("2024年9月", "本月销售额增长15%,达成季度目标");
pdfReport.display();

// 生成PDF格式的季度报告
Report quarterlyReport = director.buildQuarterlyReport("2024年Q3", "三季度总销售额同比增长12%");
quarterlyReport.display();
}
}

需要注意的是生成器模式和抽象工厂模式的不同,生成器模式更强调“如何构建”,它专注于分布构建单个复杂对象。而抽象工厂的核心是“创建什么”,它专注于创建出一套相互关联的产品。

简单说:生成器模式像 “定制蛋糕”(分步添加配料,控制每一步),抽象工厂模式像 “购买餐具套装”(一次性获取配套的碗、盘、勺)。