type
Post
status
Published
date
Oct 30, 2022
slug
builder-design-pattern-of-creation-pattern
summary
tags
建造者
category
设计模式
icon
password
一、为什么需要建造者模式
有这么一个场景,有一个资源池配置类 ResourcePoolConfig,有许多需要配置的成员变量,有的需要必填,有的不需要,有的有默认值等等,简单的实现是:
- 直接通过构造函数中添加所有的成员变量进行赋值,非必填可以传递空值即可
- 同时在构造函数中进行所有参数的合法性判断即可
更加复杂的场景下,参数或者说成员变量有很多,都通过构造函数进行赋值,可能会乱,顺序出错等等问题,同时参数合法性判断也会导致构造函数臃肿,不利于维护,因此需要有一种更好的方式来处理这种情况:即可以分离必填和非必填项:
- 构造器函数参数只设置必填的成员变量
- 非必填项都通过 set 方法进行设置
但是在一些场景下,通过简单的 set 也没办法解决问题:
- 必填参数很多,都放到构造函数中则又会出现了上面的顺序错乱易出错的问题
- 若参数有依赖关系,例如这个参数必须大于另一个参数的值,此时 set 方法中无法解决,因为必须得等所有参数都有值了才能进行校验
- 如果我们希望 ResourcePoolConfig 类对象是不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值。要实现这个功能,就不能在 ResourcePoolConfig 类中暴露 set() 方法。
为了解决上面的问题,一个好的解决方案就是使用建造者模式:
- 先创建建造者,并通过 set() 方法设置建造者的变量值
- 在使用 build() 方法真正创建对象之前,做集中的校验,校验通过之后才会创建对象
- 可以把 ResourcePoolConfig 的构造函数改为 private 私有权限,这样我们就只能通过建造者来创建 ResourcePoolConfig 类对象。
- ResourcePoolConfig 没有提供任何 set() 方法,这样我们创建出来的对象就是不可变对象了
使用建造者模式创建对象,还能避免对象存在无效状态。
如我们定义了一个长方形类,如果不使用建造者模式,采用先创建后 set 的方式,那就会导致在第一个 set 之后,对象处于无效状态(即长方形不能只有长或宽)。
可以使用构造函数传入两个参数解决,同样若必填参数过多同样会有问题,因此使用建造者模式就比较合理了。先设置建造者的变量,然后再一次性地创建对象,让对象一直处于有效状态。
建造者模式的一个问题:
使用建造者模式来构建对象,代码实际上是有点重复的,ResourcePoolConfig 类中的成员变量,要在 Builder 类中重新再定义一遍!
二、与工厂模式有什么区别
网上有一个经典的例子很好地解释了两者的区别:
顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉。对于披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨。
- 工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象
- 建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象
- 作者:NotionNext
- 链接:https://tangly1024.com/article/builder-design-pattern-of-creation-pattern
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

