1. 设计模式之构造器模式:封装复杂对象的构造逻辑
1.1. 介绍
构造器设计模式(Builder Pattern)是一种创建型设计模式,它通过分步骤构建复杂对象来解决构造器参数过多或可选参数组合复杂的问题。
该模式在开源框架及各种源码中使用较多,例如,Java 中的 StringBuilder 等。项目中如果需要构造复杂对象时可以使用该模式。
优缺点及建议
优点
1.参数组合灵活:支持可选参数自由组合,避免构造器重载(如new User(name, pwd, null, 0, ...))
2.代码可读性强:链式调用清晰表达参数含义(.name("Alice").age(25).build())
3.集中校验逻辑:在build()方法中统一校验参数,避免分散校验(如检查邮箱格式、年龄范围)
4.构造过程可控:可分步骤构建复杂对象(如先配置基础参数,再设置高级选项)
缺点
1.代码量增加:需额外编写Builder类,对简单对象可能过度设计
2.性能轻微开销:每次构建对象需创建Builder实例(但在现代JVM中影响极小)
3.学习成本 新手可能对递归泛型Builder(用于继承场景)理解困难
使用建议:
1.参数多且可选:对象有超过4个参数,且部分参数可选(如配置类、DTO)
2.复杂构造逻辑:对象构造需要分步骤或动态计算某些参数(如根据输入生成默认值)
3.DSL设计:需要流畅的API接口(如Query.builder().select(...).where(...).build())
1.2. 实现及相关代码
假设有一个复杂对象需要创建生成。
1.2.1. 没使用设计模式方式
接口及数据对象
public static class Product {
private String field1;
private String field2;
private String field3;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
}
调用方法
public static void main(String[] args) {
// 构造这个复杂的product对象
Product product = new Product();
// 设置field1属性
System.out.println("在设置field1之前进行复杂的校验逻辑");
product.setField1("值1");
// 设置field2属性
System.out.println("在设置field2之前进行复杂的数据格式转化逻辑");
product.setField2("值2");
// 设置field3属性
System.out.println("在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联");
product.setField3("值3");
}
可能会遇到的问题
问题一:
对于一些有几十个字段,甚至是上百个字段的复杂对象的构建,代码会极度膨胀,非常复杂。
问题二:
这段逻辑,如果在多个地方都有使用的话,一旦需要调整逻辑,那么可能就需要在多个地方修改这一大坨跟屎一样的代码。
1.2.2. 使用设计模式方式
使用构造器模式,它将对象的构造过程拆解为多个步骤,允许按需组合参数,避免构造器臃肿。通过返回Builder对象本身实现流畅的API(如.setA().setB().build())。
接口及数据对象
public static class Product {
private String field1;
private String field2;
private String field3;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
@Override
public String toString() {
return "Product [field1=" + field1 + ", field2=" + field2 + ", field3=" + field3 + "]";
}
}
public interface Builder {
Builder field1(String value);
Builder field2(String value);
Builder field3(String value);
Product create();
}
public static class ConcreteBuilder implements Builder {
private Product product = new Product();
public Builder field1(String value) {
System.out.println("在设置field1之前进行复杂的校验逻辑");
product.setField1(value);
return this;
}
public Builder field2(String value) {
System.out.println("在设置field2之前进行复杂的数据格式转化逻辑");
product.setField2(value);
return this;
}
public Builder field3(String value) {
System.out.println("在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联");
product.setField3(value);
return this;
}
public Product create() {
return product;
}
}
调用方法
public static void main(String[] args) {
Product product = new ConcreteBuilder()
.field1("值1")
.field2("值2")
.field3("值3")
.create();
System.out.println(product);
}
1.3. 总结
何时用Builder?
- 当对象构造涉及多个可选参数、需要不可变性或链式API时优先选择。
何时不用Builder?
- 参数极少、需要极致性能或简单数据容器时,选择更轻量级的方案。