一、前言
开发中,我们经常需要创建具有多个属性或配置选项的复杂对象。直接通过构造函数或大量的setter方法来设置这些属性可能会导致代码的可读性和维护性下降。建造者模式通过提供一个建造者类来封装复杂对象的创建过程,使得客户端代码可以更加简洁和清晰地构建对象。
二、建造者模式的使用场景
- 当对象有多个构造函数参数时:如果一个类的构造函数需要多个参数,而这些参数中有些是可选的,那么使用建造者模式可以更加清晰地表达对象的创建过程,避免构造函数的参数列表过长和混乱。
- 当对象的构建过程需要多个步骤时:如果对象的创建过程涉及多个步骤,并且这些步骤的顺序很重要,那么建造者模式可以确保这些步骤按照正确的顺序执行,并提供一种灵活的方式来调整这些步骤。
- 当需要创建不同配置的对象变体时:如果需要根据不同的配置选项创建对象的多个变体,那么建造者模式可以提供一种清晰的方式来表示这些配置选项,并根据需要构建不同的对象变体。
- 当对象的构建过程需要复杂的逻辑时:如果对象的构建过程涉及复杂的逻辑,如验证参数的有效性、计算属性的默认值等,那么建造者模式可以将这些逻辑封装在建造者类中,使客户端代码更加简洁和清晰。
三、建造者模式的三种实现方式
Java中,实现建造者设计模式通常有两种主要方式:经典的建造者模式(也称为分步构建器)和流式建造者模式(也称为流畅接口构建器或链式调用构建器)。第三种借用lomback自动生成流式建造者模式,所以实际是两种方式。
3.1 经典建造者模式
经典建造者模式通常包括一个产品类、一个抽象建造者接口、一个具体建造者类和一个指挥者类(可选)。产品类包含需要设置的属性和访问器方法。抽象建造者接口定义了构建产品所需的各个步骤。具体建造者类实现了抽象建造者接口,并提供了设置产品属性和获取构建好的产品对象的方法。
然而,在实际应用中,我们往往可以省略指挥者类,直接在客户端代码中操作具体建造者来构建产品对象。这种方式简化了经典建造者模式的结构,但仍然保留了其逐步构建对象的优点。
代码:
// 产品类 public class Product { private String partA; private String partB; private String partC; // 构造方法可以是私有的,因为建造者模式可以控制对象的构建过程 private Product() {} public String getPartA() { return partA; } public void setPartA(String partA) { this.partA = partA; } // 省略其他getter和setter方法... // 静态内部类作为具体建造者 public static class Builder { private Product product; public Builder() { product = new Product(); } public Builder setPartA(String partA) { product.partA = partA; return this; // 返回当前Builder实例,以便链式调用 } public Builder setPartB(String partB) { product.partB = partB; return this; } public Builder setPartC(String partC) { product.partC = partC; return this; } // 创建并返回产品对象 public Product build() { return product; } } } // 客户端代码 public class Client { public static void main(String[] args) { Product product = new Product.Builder() .setPartA("This is part A") .setPartB("This is part B") .setPartC("This is part C") .build(); System.out.println(product.getPartA()); // 省略访问其他部分... } }
Product 类有一个静态内部类 Builder,它负责构建 Product 对象。客户端代码通过调用 Builder 的方法来设置产品的各个部分,并最终调用 build() 方法来获取构建好的产品对象。
3.2 流式建造者模式
流式建造者模式是经典建造者模式的一种变体,它更加强调链式调用的流畅性。在流式建造者模式中,产品类通常包含一个静态内部类作为流式建造者。这个内部类提供了设置产品属性的方法,并返回自身的实例以支持链式调用。最后,通过一个build()方法返回构建好的产品对象。
流式建造者模式省略了经典建造者模式中的抽象建造者接口和指挥者类,使得代码更加简洁和直观。同时,通过链式调用的方式设置产品属性,可以提高代码的可读性和编写的灵活性。
代码:
// 产品类 public class Product { private String partA; private String partB; private String partC; // 私有构造方法,防止直接实例化 private Product() {} // 省略getter方法... // 静态内部类作为流式建造者 public static class Builder { private String partA; private String partB; private String partC; public Builder withPartA(String partA) { this.partA = partA; return this; } public Builder withPartB(String partB) { this.partB = partB; return this; } public Builder withPartC(String partC) { this.partC = partC; return this; } // 创建产品对象 public Product build() { Product product = new Product(); product.partA = this.partA; product.partB = this.partB; product.partC = this.partC; return product; } } } // 客户端代码 public class Client { public static void main(String[] args) { Product product = new Product.Builder() .withPartA("This is part A") .withPartB("This is part B") .withPartC("This is part C") .build(); // 省略访问产品属性的代码... } }
Builder 类的每个设置方法都返回 Builder 类型的实例(即 this),从而允许链式调用。这种方式使得客户端代码更加简洁和易读。注意,在这个实现中,Product 类的构造方法是私有的,以防止外部直接实例化。所有的属性设置都是通过 Builder 进行的。
3.3 使用Lombok库实现流式建造者模式
Lombok 是一个 Java 库,它可以通过注解来简化 Java 代码,例如自动生成 getter、setter、equals、hashCode 和 toString 方法等。其中,@Builder 注解就是用来实现建造者模式的一个简化工具。
使用 Lombok 的 @Builder
注解,你不需要手动编写建造者模式的代码。只需要在类定义上添加 @Builder
注解,Lombok 就会在编译时自动生成相应的建造者类。
下面是一个使用 Lombok @Builder
注解来实现建造者模式的代码:
import lombok.Builder; import lombok.Getter; import lombok.Setter; @Getter @Setter @Builder public class Person { private String name; private int age; private String address; // 使用 Lombok @Builder 注解后,无需再手动编写建造者模式的代码 // Lombok 会在编译时自动生成一个名为 Person.PersonBuilder 的内部类 // 下面是如何使用生成的建造者类来构建 Person 对象的示例 public static void main(String[] args) { Person person = Person.builder() .name("John Doe") .age(30) .address("123 Main St") .build(); System.out.println(person); } }
在这个例子中,我们定义了一个 Person 类,并使用了 @Getter、@Setter 和 @Builder 注解。@Getter 和 @Setter 注解分别用于生成 getter 和 setter 方法,而 @Builder 注解则用于生成建造者模式的代码。
在 main 方法中,我们使用 Person.builder() 方法来获取一个 PersonBuilder 实例,然后通过链式调用设置 name、age 和 address 属性,最后调用 build() 方法来构建 Person 对象。
以上使用 Lombok 的 @Builder 注解所生成的代码在编译时会创建一个内部类,这个内部类通常命名为 类名Builder,在这个例子中是 PersonBuilder。这个内部类会包含对应类中所有字段的设置方法以及一个 build() 方法来创建目标对象。
生成的代码大致相当于手动实现的流式建造者模式,因为它允许通过链式调用的方式来设置对象的属性。不过,由于 Lombok 自动处理了这些细节,用户无需手动编写这些代码。
假设没有 Lombok,并且我们要手动实现上述 Person
类的建造者模式,代码可能看起来像这样:
public class Person { private String name; private int age; private String address; private Person(Builder builder) { this.name = builder.name; this.age = builder.age; this.address = builder.address; } public static class Builder { private String name; private int age; private String address; public Builder() {} public Builder name(String name) { this.name = name; return this; } public Builder age(int age) { this.age = age; return this; } public Builder address(String address) { this.address = address; return this; } public Person build() { return new Person(this); } } // getters, setters, toString, etc. }
使用 Lombok,上面的所有建造者逻辑都会在编译时自动生成,你不需要手动编写这些代码。Lombok 会创建一个与上述手动实现的 Builder 类类似的内部类,并提供同样的链式调用功能。
四、建造者模式的注意事项
- 避免滥用建造者模式:虽然建造者模式可以提供一种清晰和灵活的方式来构建复杂对象,但过度使用它可能会导致代码变得复杂和难以维护。因此,在决定使用建造者模式之前,应该仔细评估对象的复杂性和构建过程的需求。
- 考虑建造者的可重用性:如果对象的构建过程涉及一些可重用的步骤或组件,那么可以设计可重用的建造者类来减少代码重复和提高可维护性。例如,可以创建一些通用的建造者类来处理常见的构建步骤,并在需要时进行扩展或定制。
- 注意与工厂模式的区别:建造者模式与工厂模式都是创建型设计模式,但它们的职责和用途有所不同。工厂模式主要负责对象的创建和实例化,而建造者模式则关注于对象的构建过程和属性的设置。在实际应用中,可以根据需要选择使用哪种模式或结合使用它们。
五、结语
- 建造者模式是一种强大且灵活的设计模式,适用于构建具有多个属性和复杂构建过程的对象。
- 通过合理使用建造者模式,可以提高代码的可读性、可维护性和可扩展性。
- 然而,在使用建造者模式时,也需要注意避免滥用、考虑线程安全问题以及与其他设计模式的区别和协作。
- 只有在合适的场景下使用建造者模式,才能充分发挥其优势并提升软件的质量。
经典建造者模式和流式建造者模式都是用于封装复杂对象创建过程的有效方式。经典建造者模式通过抽象建造者接口和具体建造者类的分离,实现了构建过程的灵活性和可扩展性。而流式建造者模式则通过链式调用的方式,提高了代码的可读性和编写的便捷性。
在选择使用哪种建造者模式时,需要根据具体的需求和场景来判断。如果需要较高的灵活性和可扩展性,可以考虑使用经典建造者模式;如果追求代码的简洁和易读性,流式建造者模式可能是一个更好的选择。