设计模式之生成实例_Builder模式_组装复杂的实例
前言
Github:https://github.com/HealerJean
一、建造者(Builder Pattern)设计模式
1、模式概述
建造者模式 是一种 创建型设计模式,它将一个 复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。 客户端只需指定所需产品的类型,而无需关心其内部构造细节。
核心思想:“分步构建,统一组装 —— 复杂对象的创建交给专业‘建造者’。”
建造者模式 = 分步构建 + 统一组装:
- 它不是“一次性造出来”,而是“一步步搭起来”,让复杂对象的创建变得可控、可读、可维护。
在实际开发中:
JavaStringBuilder是 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、具体打包实现
命名优化:PackWrapper → Wrapper,更符合领域语言。
/** 汉堡类使用塑料袋包装 */
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更高效;


