Skip to content
难度基础(★)
建议时长45分钟

12.5.2 例题2:生成器模式

本节例题是快餐厅制作儿童套餐:套餐一般包含主餐、饮料、玩具等部件,不同套餐内容不同,但制作流程相似。课程用这个场景讲生成器模式,并借题训练 Java 抽象类、this、setter、方法调用和主函数组合调用。

生成器模式的核心场景

生成器模式也叫建造者模式。它适合“构建步骤稳定,但最终产品表示可能不同”的情况。

场景元素在例题中的含义在模式中的角色
儿童套餐/Pizza最终要得到的复杂对象Product
PizzaBuilder定义制作 Pizza 的步骤Builder
HawaiianPizzaBuilder具体制作某种 PizzaConcreteBuilder
Waiter/服务员调度制作流程Director
mermaid
sequenceDiagram
  participant Client as 点餐客户端
  participant Waiter as 服务员(Director)
  participant Builder as PizzaBuilder
  participant Product as Pizza
  Client->>Waiter: setPizzaBuilder(builder)
  Client->>Waiter: constructPizza()
  Waiter->>Builder: createNewPizza()
  Waiter->>Builder: buildParts()
  Client->>Waiter: getPizza()
  Waiter->>Builder: getPizza()
  Builder-->>Product: 返回产品

为什么要有 Director

如果客户端直接创建产品,就会把“先创建对象、再构造部件、最后取出产品”的流程散落在各处。生成器模式把流程收束到 Director 中。客户端只需要把具体 Builder 交给 Director,Director 负责按固定顺序调用步骤。

这种设计的价值在于:

问题不用生成器使用生成器
构建步骤多客户端到处写重复流程Director 统一流程
产品表示多每种产品都可能有一套混乱创建代码新增 ConcreteBuilder
调用顺序重要容易漏步骤或顺序错Director 固化顺序

代码结构如何读

课程中例题的典型结构可以抽象成下面这样:

java
class Pizza {
    private String parts;
}

abstract class PizzaBuilder {
    protected Pizza pizza;

    public Pizza getPizza() {
        return pizza;
    }

    public void createNewPizza() {
        pizza = new Pizza();
    }

    public abstract void buildParts();
}

class HawaiianPizzaBuilder extends PizzaBuilder {
    public void buildParts() {
        // 构造夏威夷 Pizza 的部件
    }
}

class Waiter {
    private PizzaBuilder pizzaBuilder;

    public void setPizzaBuilder(PizzaBuilder pizzaBuilder) {
        this.pizzaBuilder = pizzaBuilder;
    }

    public Pizza getPizza() {
        return pizzaBuilder.getPizza();
    }

    public void constructPizza() {
        pizzaBuilder.createNewPizza();
        pizzaBuilder.buildParts();
    }
}

抽象方法空:为什么不能只写方法名

课程特别强调:如果一个类被声明为抽象类,通常至少包含一个抽象方法。例题中的 PizzaBuilder 是抽象类,它的具体子类实现了 buildParts(),因此父类中缺失的方法不是普通方法,而是抽象方法。

错误写法错误原因
void buildParts();在 Java 抽象类中,缺少 abstract 修饰
public void buildParts() {}这变成了有方法体的普通方法,违背题目结构
abstract buildParts();缺少返回值类型
public abstract void buildParts();正确

注意:如果题目已经给了 public,填空时就不要重复写 public,只补缺失部分。

setter 空:为什么要写 this

setPizzaBuilder(PizzaBuilder pizzaBuilder) 中,参数名和成员变量名相同。左侧要表示当前对象的成员变量,右侧表示传入的参数,所以写成:

java
this.pizzaBuilder = pizzaBuilder;

这类空不是模式题,是 Java 基础语法题。看到 setter,要马上找:

  1. 成员变量叫什么。
  2. 参数叫什么。
  3. 两者是否同名。
  4. 同名时左边是否需要 this.

Director 调用顺序

Waiter.constructPizza() 中,课程引导大家从当前可用对象出发:Waiter 内部有 pizzaBuilder 成员,PizzaBuilder 自带 getPizza()createNewPizza()buildParts() 等方法。构建过程不能只设置 Builder,还要真正调用构建步骤。

mermaid
flowchart TD
  A["waiter.setPizzaBuilder(hawaiianBuilder)"] --> B["waiter.constructPizza()"]
  B --> C["pizzaBuilder.createNewPizza()"]
  C --> D["pizzaBuilder.buildParts()"]
  D --> E["waiter.getPizza()"]

主函数中的组合调用

主函数常见代码形态如下:

java
Waiter waiter = new Waiter();
PizzaBuilder hawaiianBuilder = new HawaiianPizzaBuilder();

waiter.setPizzaBuilder(hawaiianBuilder);
waiter.constructPizza();
Pizza pizza = waiter.getPizza();

这里最关键的是两次动作不要混淆:

动作含义
setPizzaBuilder(...)把具体生成器交给服务员
constructPizza()让服务员调度生成器开始构建
getPizza()从生成器中取出已经构建好的产品

只设置生成器并不会自动创建产品;必须调用构建方法。

生成器与其他创建方式的比较

方式适用情况优点局限
构造函数直接创建参数少、对象简单简洁直接参数多时可读性差,步骤不可控
工厂方法只关心创建哪一类产品隐藏具体类不强调复杂步骤
抽象工厂创建一族相关产品保证产品族一致类体系较重
生成器构建步骤多且步骤稳定构建过程与表示分离对简单对象显得繁琐

生成器不是“更高级所以替代构造函数”,而是在对象构造变复杂后,用更清晰的流程替代臃肿构造函数和散乱初始化代码。

代码填空定位表

空缺现象先看哪里可能答案
抽象类中缺方法看具体子类实现的方法abstract void buildParts();
setter 方法体缺失看成员变量和参数this.pizzaBuilder = pizzaBuilder;
construct 方法体缺失看 Builder 暴露的方法pizzaBuilder.buildParts();
主函数缺调用看输出前是否已设置和构建waiter.setPizzaBuilder(builder);waiter.constructPizza();

例题

单选
在生成器模式中,负责安排构建步骤顺序的角色通常是:

自查清单

  1. 能否说明生成器模式分离的是“构建过程”和“产品表示”?
  2. 能否从具体子类反推抽象类中的 abstract 方法?
  3. 能否解释 this.pizzaBuilder = pizzaBuilder 中左右两边的区别?
  4. 能否写出 Director 的三步流程:设置 Builder、构建、取产品?