前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、建造者(Builder Pattern)设计模式

1、模式概述

建造者模式 是一种 创建型设计模式,它将一个 复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。 客户端只需指定所需产品的类型,而无需关心其内部构造细节。

核心思想“分步构建,统一组装 —— 复杂对象的创建交给专业‘建造者’。”

建造者模式 = 分步构建 + 统一组装

  • 它不是“一次性造出来”,而是“一步步搭起来”,让复杂对象的创建变得可控、可读、可维护。

在实际开发中:

  • Java StringBuilder 是 Builder 模式的经典应用;
  • Lombok@Builder 注解可自动生成建造者代码;
  • 任何“多参数、多组件、多组合”的对象创建场景,都值得考虑此模式。

2、使用场景

建造者模式适用于以下情况:

  • 对象的创建过程 复杂且步骤固定(如多属性、多组件);
  • 同一构建过程可产生 多种不同表现形式 的产品;
  • 希望 隐藏对象创建细节,提供简洁的使用接口;
  • 需要 不可变对象(Immutable),但构造参数过多(避免 telescoping constructor 问题)。

典型例子

  • 游戏角色创建(装备、技能、外观);
  • HTTP 请求构建(URL、Header、Body);
  • 文档生成(标题、段落、图表)。

3、示例程序:肯德基套餐构建

  • 本示例采用 简化版建造者(无显式 Builder 接口),适用于产品种类较少的场景;
  • 若需更高灵活性(如链式调用、自定义步骤),应引入抽象 Builder 接口。
角色 职责 示例
Product(产品) 被构建的复杂对象 Meal
Builder(抽象建造者) 定义构建步骤的接口(可选) (本例中隐含在 MealBuilder 中)
ConcreteBuilder(具体建造者) 实现构建步骤,返回完整产品 MealBuilder
Director(指挥者,可选) 封装构建流程,协调 Builder 步骤 (本例中由 MealBuilder 自包含)
Client(客户端) 使用 Builder 获取最终产品 Main

1)产品组件接口 Item

/**
 * 商品接口 —— 所有食物项的公共行为
 */
public interface Item {
    String name();      // 商品名称
    float price();      // 商品价格
    Pack packing();     // 打包方式
}

2)打包策略

a、打包接口 Pack

public interface Pack {
    String pack();
}

b、具体打包实现

命名优化PackWrapperWrapper,更符合领域语言。

/** 汉堡类使用塑料袋包装 */
public class Wrapper implements Pack {
    @Override
    public String pack() {
        return "塑料袋";
    }
}

/** 饮料类使用瓶装 */
public class Bottle implements Pack {
    @Override
    public String pack() {
        return "瓶装";
    }
}

3)具体食物项(Concrete Products)

a、汉堡基类

public abstract class Burger implements Item {
    @Override
    public Pack packing() {
        return new Wrapper();
    }
}

b、饮料基类

public abstract class ColdDrink implements Item {
    @Override
    public Pack packing() {
        return new Bottle();
    }
}

c、具体食物实现

命名规范:类名使用名词(VegBurger 而非 BurgerVeg),更自然。

public class VegBurger extends Burger {
    @Override public float price() { return 25.0f; }
    @Override public String name() { return "蔬菜汉堡"; }
}
å
public class ChickenBurger extends Burger {
    @Override public float price() { return 50.5f; }
    @Override public String name() { return "鸡腿汉堡"; }
}

public class Coke extends ColdDrink {
    @Override public float price() { return 30.0f; }
    @Override public String name() { return "可口可乐"; }
}

public class Pepsi extends ColdDrink {
    @Override public float price() { return 35.0f; }
    @Override public String name() { return "百事可乐"; }
}

4)最终产品类 Meal

import java.util.ArrayList;
import java.util.List;

/**
 * 套餐 —— 最终构建的复杂产品
 */
public class Meal {
    private final List<Item> items = new ArrayList<>();

    public void addItem(Item item) {
        items.add(item);
    }

    public float getCost() {
        return items.stream().mapToFloat(Item::price).sum();
    }

    public void showItems() {
        items.forEach(item -> 
            System.out.printf("商品名称: %s, 打包方式: %s, 价格: %.1f%n",
                item.name(), item.packing().pack(), item.price())
        );
    }
}

5)建造者类 MealBuilder

设计意图:客户端无需知道套餐包含哪些单品,只需调用 prepareXxxMeal() 即可获得完整套餐。

/**
 * 套餐建造者å —— 封装不同套餐的构建逻辑
 */
public class MealBuilder {

    public Meal prepareVegMeal() {
        Meal meal = new Meal();
        meal.addItem(new VegBurger());
        meal.addItem(new Coke());
        return meal;
    }

    public Meal prepareNonVegMeal() {
        Meal meal = new Meal();
        meal.addItem(new ChickenBurger());
        meal.addItem(new Pepsi());
        return meal;
    }
}

6)客户端测试 Main

效果:客户端代码简洁,完全屏蔽了套餐内部组合逻辑。

public class Main {
    public static void main(String[] args) {
        MealBuilder builder = new MealBuilder();

        Meal vegMeal = builder.prepareVegMeal();
        System.out.println("=== 套餐一(素食)===");
        vegMeal.showItems();
        System.out.printf("总价格: %.1f%n%n", vegMeal.getCost());

        Meal nonVegMeal = builder.prepareNonVegMeal();
        System.out.println("=== 套餐二(荤食)===");
        nonVegMeal.showItems();
        System.out.printf("总价格: %.1f%n", nonVegMeal.getCost());
    }
}

输出:

=== 套餐一(素食)===
商品名称: 蔬菜汉堡, 打包方式: 塑料袋, 价格: 25.0
商品名称: 可口可乐, 打包方式: 瓶装, 价格: 30.0
总价格: 55.0

=== 套餐二(荤食)===
商品名称: 鸡腿汉堡, 打包方式: 塑料袋, 价格: 50.5
商品名称: 百事可乐, 打包方式: 瓶装, 价格: 35.0
总价格: 85.5

4、FQA

1)与外观模式的区别

  • 建造者:“我要造一个新东西,步骤很多,你帮我一步步搭”;
  • 外观:“这些底层 API 太乱了,给我一个统一入口就行”。
模式 目的 是否创建新对象 是否支持多种结果
建造者模式(Builder) 分步构建复杂对象 ✅ 是(全新实例) ✅ 支持(不同建造者 → 不同产品)
外观模式(Facade) 简化子系统调用接口 ❌ 否(仅封装现有调用) ❌ 通常返回固定结果

2)模式优点

  • 解耦构建与表示:构建逻辑集中管理;
  • 易于扩展:新增套餐只需添加新方法;
  • 代码可读性强:客户端调用语义清晰;
  • 支持不可变对象:若 Meal 字段设为 final,可保证线程安全。

3)注意事项

  • 类数量增加:每个产品组件需独立类;
  • 过度设计风险:若对象结构简单,直接 new 更高效;