构建器模式(创建型模式)
原创大约 5 分钟
模式概述
作为以一种创建型模式,构建器模式可以分步骤地创建复杂对象,并使用相同的代码创建不同类型和形式的对象。
程序员们应该经常可以在一些第三方的组件中看到构建器模式的应用。
/**
* 某第三方推送应用设置
*
*/
public static PushPayload buildPushObject(String alias, String content) {
return PushPayload.newBuilder()
// 所有平台
.setPlatform(Platform.all())
// 向选定的人推送
.setAudience(Audience.alias(alias))
// 消息内容
.setNotification(Notification.alert(content))
.build();
}

问题
有一个较为复杂的对象,在创建时需要对它的诸多属性和嵌套对象进行复杂的初始化工作,这一初始化过程使用一个参数众多、较为复杂的构造函数中,而且这种构造函数还有很多个,都散落在各个调用它的地方。
例如,如果要盖一座房子,除了地面、天花板、墙壁、门窗、水电,如果还想要其他的附加功能呢?

车库、花园、泳池和酒窖,每一种功能所需要的参数
都会和其他功能的参数
不同,一种办法是把这些参数
全都集中起来,然后按需分配
,就像这样:

采用这种方式,会让一些参数毫无用处,这样既不简洁,也不优雅。
方案
构建器针对每一种不同的产品
,将它们所需的参数
抽取出来,放在一个名为构建器的独立对象中。
该对象会将产品的构建过程分为一系列的步骤,确保每一步都是产品所必需的。

结构

实现
Java
/**
* 通用构建器接口
*
*/
public interface Builder {
void setHouseType(HouseType type);
void setFloor(int floor);
void setDoors(Doors doors);
void setWindows(Windows windows);
void setGarden(Garden garden);
void setGarage(Garage garage);
}
/**
* 花园房构建器
*
*/
public class HouseGarden implements Builder {
private HouseType type;
private int floor;
private Doors doors;
private Windows windows;
private Garden garden;
// setter
// ......
public House getResult() {
return new House(type, floor, doors, windows, garden);
}
}
/**
* 房子
*
*/
public class House {
private final HouseType houseType;
private final int floor;
private final Doors doors;
private final Windows windows;
private final Garden garden;
private final Garage garage;
public House(HouseType houseType, int floor, Doors doors, Windows windows,
Garden garden, Garage garage) {
this.houseType = houseType;
this.floor = floor;
this.doors = doors;
this.windows = windows;
this.garden = garden;
this.garage = garage;
}
// getter && setter
// ......
}
/**
* 引导类
*
*/
public class Director {
public void constructHouseGarden(Builder builder) {
builder.setHouseType(HouseType.GARDEN);
builder.setFloor(100);
builder.setDoors(new Doors(5));
builder.setWindows(new Windows(10));
builder.setGarden(new Garden());
}
public void constructHouseGarage(Builder builder) {
builder.setHouseType(HouseType.GARAGE);
builder.setFloor(100);
builder.setDoors(new Doors(5));
builder.setWindows(new Windows(10));
builder.setGarage(new Garage());
}
......
}
/**
* 客户端
*
*/
public class Client {
public static void main(String[] args) {
Director director = new Director();
// 从客户端获得具体的构建器对象
HouseGarden builder = new HouseGarden();
director.constructHouseGarden(builder);
// 最终产品是从构建器对象中获取的,因为引导类不知道,也不依赖具体构建器和产品
House house = builder.getResult();
System.out.println("House built:\n" + house.getHouseType());
}
}
Go
package main
import "fmt"
/**
* 房屋生成器接口
*
*/
type IBuilder interface {
setWindowType()
setDoorType()
setNumFloor()
getHouse() House
}
func getBuilder(builderType string) IBuilder {
if builderType == "normal" {
return newNormalBuilder()
}
if builderType == "sangna" {
return newSangnaBuilder()
}
return nil
}
/**
* 通用房屋生成器
*
*/
type NormalBuilder struct {
windowType string
doorType string
floor int
}
func newNormalBuilder() *NormalBuilder {
return &NormalBuilder{}
}
func (b *NormalBuilder) setWindowType() {
b.windowType = "木窗户"
}
func (b *NormalBuilder) setDoorType() {
b.doorType = "木门"
}
func (b *NormalBuilder) setNumFloor() {
b.floor = 2
}
func (b *NormalBuilder) getHouse() House {
return House{
doorType: b.doorType,
windowType: b.windowType,
floor: b.floor,
}
}
/**
* 桑拿房生成器
*
*/
type SangnaBuilder struct {
windowType string
doorType string
floor int
}
func newSangnaBuilder() *SangnaBuilder {
return &SangnaBuilder{}
}
func (b *SangnaBuilder) setWindowType() {
b.windowType = "没有窗户"
}
func (b *SangnaBuilder) setDoorType() {
b.doorType = "密封门"
}
func (b *SangnaBuilder) setNumFloor() {
b.floor = 1
}
func (b *SangnaBuilder) getHouse() House {
return House{
doorType: b.doorType,
windowType: b.windowType,
floor: b.floor,
}
}
/**
* 房屋产品
*
*/
type House struct {
windowType string
doorType string
floor int
}
/**
* 引导类
*
*/
type Director struct {
builder IBuilder
}
func newDirector(b IBuilder) *Director {
return &Director{
builder: b,
}
}
func (d *Director) setBuilder(b IBuilder) {
d.builder = b
}
func (d *Director) buildHouse() House {
d.builder.setDoorType()
d.builder.setWindowType()
d.builder.setNumFloor()
return d.builder.getHouse()
}
/**
* 客户端
*
*/
func main() {
normalBuilder := getBuilder("normal")
sangnaBuilder := getBuilder("sangna")
director := newDirector(normalBuilder)
normalHouse := director.buildHouse()
fmt.Printf("普通房子的门:%s\n", normalHouse.doorType)
fmt.Printf("普通房子的窗户:%s\n", normalHouse.windowType)
fmt.Printf("普通房子的地板:%d\n", normalHouse.floor)
director.setBuilder(sangnaBuilder)
sangnaHouse := director.buildHouse()
fmt.Printf("\n桑拿房的门:%s\n", sangnaHouse.doorType)
fmt.Printf("桑拿房的窗户:%s\n", sangnaHouse.windowType)
fmt.Printf("桑拿房的地板:%d\n", sangnaHouse.floor)
}
适用场景
当某个类构造参数较多,且产品需要通过这些参数
组合
的形式来产生时,就可以使用构建器模式。当需要通过代码实现每一步都可配置的应用时,可以使用构建器模式。
优缺点
构建器模式的优点。
向调用者隔绝创建产品的细节,将产品与其创建过程解耦。
可以很精细地控制构建过程,也可以很方便地替换具体构件者或增加新的构建者,符合
开闭原则
。
构建器模式的缺点。
- 如果产品之间的差异较小使用构建器模式反而有些麻烦。
如果产品内部变化复杂,可能会导致需要定义很多具体构建者,让系统变复杂。
相关性
可以在创建
组合模式
时使用构建器模式
,这可使其以递归的方式构造运行。可以结合使用
构建器模式
和桥接模式
: 引导类完成抽象工作,而构建器则负责实现工作。工厂方法模式
可以演化为抽象工厂模式
、原型模式
和构建器
模式。抽象工厂模式
、构建器模式
和原型模式
都可以用单例模式
来实现。
感谢支持
更多内容,请移步《超级个体》。