← 返回题目列表

工厂方法模式和建造者模式应该如何取舍?

高频 中等 第 5 / 25 题 更新于 2026/08/01
工厂方法模式建造者模式Builder模式创建型模式

简化版

工厂方法模式关注“创建哪一种产品”,适合产品类型可扩展、调用方不想依赖具体类的场景;建造者模式关注“一个复杂对象如何分步骤组装”,适合参数多、可选项多、构造过程复杂的场景。前者解决产品选择,后者解决对象组装。

详细版

工厂方法和建造者都属于创建型模式,但关注点不同。工厂方法把实例化延迟给具体工厂,用多态选择具体产品;建造者把复杂对象的构造步骤拆开,避免长构造器和参数组合混乱。

例如支付渠道 WechatPaymentAlipayPaymentCardPayment 的选择,更适合工厂方法或注册表工厂;而创建一个 HttpRequest,需要 URL、headers、timeout、body、retry、proxy 等大量可选参数,更适合 Builder。

维度工厂方法建造者模式
核心问题选择哪种产品如何组装复杂对象
产品数量多种产品/实现通常是一个复杂产品
参数复杂度不一定复杂通常参数多、步骤多
扩展点新增具体产品和工厂新增构造步骤或配置项

它们也可以组合:工厂方法先选择具体产品类型,产品内部再用 Builder 组装复杂参数。

完整版教学

一、两个模式的关注点完全不同

工厂方法模式的关键词是“多态选择”。调用方知道自己要一个抽象产品,但不想知道具体类。建造者模式的关键词是“分步组装”。调用方知道要构造某一类复杂对象,但不想面对巨大的构造器参数列表。

工厂方法:我要 Parser,但具体是 JsonParser 还是 XmlParser 由工厂决定
建造者:我要 HttpRequest,但它的 headers、body、timeout 要逐步配置

如果面试时只说“它们都是创建对象的模式”,答案会显得太浅。真正要比较的是创建复杂度来自类型选择,还是来自参数组装。

二、工厂方法更适合产品类型变化

当系统里有多种实现,并且新增实现是常态,就适合把创建逻辑放到工厂层。比如消息发送支持短信、邮件、站内信、企业微信,业务流程只依赖 MessageSender

public interface SenderFactory {
    MessageSender createSender();
}

public class SmsSenderFactory implements SenderFactory {
    public MessageSender createSender() {
        return new SmsSender();
    }
}

假设 6 个月新增 5 种发送渠道,如果业务代码到处 new SmsSender(),每次都要修改调用方;如果依赖工厂抽象,新增具体工厂和产品即可减少对旧流程的影响。

三、建造者更适合参数组合复杂

当产品类型不多,但构造参数很多时,Builder 更合适。比如一个 HTTP 请求对象可能有 3 个必填参数和 10 个可选参数。用构造器会出现参数顺序难记、重载爆炸和可读性差的问题。

HttpRequest request = HttpRequest.builder()
    .url("https://api.example.com")
    .method("POST")
    .timeoutMillis(3000)
    .header("Trace-Id", "abc")
    .body("{\"id\": 1}")
    .build();

这里的重点不是选择 HttpRequest 的子类,而是让构造过程清晰、可校验、可维护。Builder 还能在 build() 阶段统一检查必填项,比如 URL 不能为空、超时时间必须大于 0。

四、用数字看构造器重载为什么会失控

假设一个对象有 8 个可选参数,每个参数都有“传或不传”两种状态,理论组合数是:

2^8 = 256

当然不会真的写 256 个构造器,但这说明重载构造器很快会失控。Builder 通过链式设置只表达用户真正关心的参数,避免大量无意义重载。

工厂方法面对的不是这个问题。工厂方法即使产品有 8 个参数,也不天然解决参数可读性;它只是决定创建哪个具体产品。必要时,具体产品创建过程内部仍可以用 Builder。

五、两者可以组合,不必二选一

复杂系统里经常先用工厂选择产品,再用 Builder 组装参数。例如不同云厂商的客户端创建:

CloudClientFactory -> 选择 AwsClient / AliyunClient / TencentClient
ClientBuilder      -> 配置 region、timeout、retry、credential

代码可能是:

CloudClient client = factory.createBuilder()
    .region("cn-hangzhou")
    .timeoutMillis(5000)
    .retry(3)
    .build();

这种组合说明模式不是标签比赛。工厂解决“哪一类”,Builder 解决“怎么配”。只要职责清晰,组合使用是合理的。

记忆钩子:工厂方法管“选型号”,建造者管“配参数”。

六、错误取舍会带来什么问题

如果把 Builder 用来做大量类型选择,可能会出现 builder.type("sms") 这种隐藏分支,最后还是一个参数化工厂,只是披着 Builder 外衣。如果把工厂方法用来处理大量可选参数,又会出现工厂方法参数越来越长的问题。

对比:

错误做法后果
用 Builder 的 type 字段选择大量产品分支隐藏,扩展仍要改旧代码
用工厂方法接收十几个可选参数参数混乱,调用可读性差
为简单对象强行 Builder代码膨胀,收益很低
为固定类型强行多工厂类数量增加,理解成本上升

设计模式的取舍要看变化点。类型是变化点,用工厂;构造步骤是变化点,用 Builder。

七、面试答题顺序

可以按四步回答:

  1. 先给结论:工厂方法管产品选择,Builder 管复杂组装。
  2. 给例子:支付渠道适合工厂,HTTP 请求适合 Builder。
  3. 讲组合:工厂返回具体 Builder 或产品内部使用 Builder。
  4. 讲误用:不要用 type 字段把 Builder 写成大分支,也不要让工厂方法带十几个参数。

这样回答既有概念区分,又有工程判断,比只背定义更像真实项目经验。

八、常见误区与追问

  • 误区:工厂方法和 Builder 都是创建对象,所以可以随便替换。 它们解决的变化点不同,不能只按创建型模式归类。
  • 误区:Builder 一定比工厂方法更高级。 简单产品选择用工厂更直接,Builder 主要解决参数和步骤复杂。
  • 误区:工厂方法可以优雅解决长参数列表。 它不解决参数可读性,长参数仍应考虑 Builder 或参数对象。
  • 误区:Builder 里加 type 字段就是工厂方法。 这通常只是把 if-else 藏进 Builder。
  • 追问:两者能否一起使用? 可以,工厂选择具体产品或 Builder,Builder 负责组装细节。
  • 追问:抽象工厂和 Builder 又怎么区分? 抽象工厂关注产品族一致创建,Builder 关注一个复杂对象的分步构造。
  • 追问:简单对象是否要 Builder? 参数少且语义清楚时构造器或静态工厂方法更轻量。

九、加强记忆

把创建复杂度分成两类:类型复杂和参数复杂。类型复杂用工厂方法或注册表工厂,参数复杂用 Builder;两者同时存在时可以组合。面试时用“支付渠道”和“HTTP 请求”两个例子,基本能把取舍讲清楚。