建造者模式(Builder Pattern)是一种创建型设计模式,它用于构建复杂对象,将对象的构建过程与其表示分离,这样可以创建不同类型的对象,而不必直接暴露对象的内部细节。这有助于简化对象的构建,提高可维护性,并允许灵活地配置对象的各个部分。
由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
建造者模式可以将部件和其组装过程分开,一步一步创建一 个复杂的对象。 用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。
结构
建造者模式的主要组成部分有:
-
Director(指导者):Director负责管理建造者对象,并定义构建过程的顺序。它接收客户端的请求,并协调建造者来构建对象。Director通常不直接构建对象,而是依赖于具体的建造者来执行实际的构建步骤。
-
Builder(建造者):Builder接口定义了构建复杂对象所需的方法。通常,一个抽象的Builder接口包含多个方法,每个方法用于构建对象的一个部分。具体的建造者类实现了这个接口,根据具体的需求来实现这些方法,负责构建对象的各个部分。
-
ConcreteBuilder(具体建造者):具体建造者是实现了Builder接口的具体类。每个具体建造者负责构建特定类型的对象,它包含了构建对象的逻辑和算法。具体建造者通常具有一个与之关联的产品实例,并且在构建过程中逐步组装这个产品。
-
Product(产品):产品是由建造者构建的复杂对象。产品类通常包含了对象的各个部分,并提供了访问这些部分的方法。
实例
下面以汽车生产作为例子演示建造者模式
Car类
java
class Car {
private String make;
private String model;
private int year;
private boolean isElectric;
private boolean hasSunroof;
public Car(String make, String model, int year, boolean isElectric, boolean hasSunroof) {
this.make = make;
this.model = model;
this.year = year;
this.isElectric = isElectric;
this.hasSunroof = hasSunroof;
}
@Override
public String toString() {
return "Car{" +
"make='" + make + '\'' +
", model='" + model + '\'' +
", year=" + year +
", isElectric=" + isElectric +
", hasSunroof=" + hasSunroof +
'}';
}
}
建造者接口(Builder)
定义一个汽车建造者接口
java
interface CarBuilder {
CarBuilder setMake(String make);
CarBuilder setModel(String model);
CarBuilder setYear(int year);
CarBuilder setElectric(boolean isElectric);
CarBuilder setSunroof(boolean hasSunroof);
Car build();
}
具体建造者(ConcreteBuilder)
创建汽车具体建造者类
java
class CarBuilderImpl implements CarBuilder {
private String make;
private String model;
private int year;
private boolean isElectric;
private boolean hasSunroof;
@Override
public CarBuilder setMake(String make) {
this.make = make;
return this;
}
@Override
public CarBuilder setModel(String model) {
this.model = model;
return this;
}
@Override
public CarBuilder setYear(int year) {
this.year = year;
return this;
}
@Override
public CarBuilder setElectric(boolean isElectric) {
this.isElectric = isElectric;
return this;
}
@Override
public CarBuilder setSunroof(boolean hasSunroof) {
this.hasSunroof = hasSunroof;
return this;
}
@Override
public Car build() {
return new Car(make, model, year, isElectric, hasSunroof);
}
}
指导者类(Director)
创建指导者类,用于·知道建造者构建汽车
java
class CarDirector {
public Car buildSportsCar() {
return new CarBuilderImpl()
.setMake("Porsche")
.setModel("911")
.setYear(2023)
.setElectric(false)
.setSunroof(true)
.build();
}
public Car buildElectricSUV() {
return new CarBuilderImpl()
.setMake("Tesla")
.setModel("Model X")
.setYear(2023)
.setElectric(true)
.setSunroof(true)
.build();
}
}
顾客测试类
创建一个顾客类Customer,测试建造者模式
java
public class Customer {
public static void main(String[] args) {
CarDirector director = new CarDirector();
Car sportsCar = director.buildSportsCar();
System.out.println("Sports Car: " + sportsCar);
Car electricSUV = director.buildElectricSUV();
System.out.println("Electric SUV: " + electricSUV);
}
}
通过上面的例子可以很清晰的理解建造者模式,然后,代码还是有点太多了,可以简化一下代码,即将抽象建造者、具体建造者、指导类合并成一个建造者类
java
class Builder {
private String make;
private String model;
private int year;
private boolean isElectric;
private boolean hasSunroof;
public Builder make(String make) {
this.make = make;
return this;
}
public Builder model(String model) {
this.model = model;
return this;
}
public Builder year(int year) {
this.year = year;
return this;
}
public Builder electric(boolean isElectric) {
this.isElectric = isElectric;
return this;
}
public Builder sunroof(boolean hasSunroof) {
this.hasSunroof = hasSunroof;
return this;
}
public Car build() {
return new Car(make, model, year, isElectric, hasSunroof);
}
}
测试
java
public class Main {
public static void main(String[] args) {
Builder builder = new Builder();
Car car = builder
.make("Toyota")
.model("Camry")
.year(2022)
.electric(false)
.sunroof(true)
.build();
System.out.println(car);
}
}
优点
建造者模式有以下优点:
-
分离构建过程和表示: 建造者模式将对象的构建过程与其表示分离,允许你以相同的构建过程创建不同的表示。这使得你可以更容易地添加新的表示或更改现有表示,而无需更改构建过程。
-
可读性和可维护性: 建造者模式的方法链(链式调用)使代码更加清晰和易于理解。每个方法都描述了构建过程的一部分,有助于提高可读性和可维护性。
-
避免构造函数参数过多: 当一个对象具有大量属性和配置选项时,使用构造函数传递所有这些参数会导致构造函数的参数列表变得非常长。建造者模式通过将属性设置过程拆分为多个方法来避免这个问题。
-
可选配置项: 如果对象具有许多可选配置项,而不是所有配置项都是必需的,那么建造者模式使得可以根据需要选择配置,而不必担心忽略或混淆不需要的配置。
-
灵活性: 建造者模式允许在构建过程中进行灵活的配置和修改。你可以根据需要随时添加新的配置选项或步骤,而无需更改客户端代码。
-
提供清晰的接口: 建造者模式为客户端提供了一个清晰的接口,客户端只需按照预定的步骤调用方法来构建对象,而无需了解构建的细节。
缺点
同时,建造者模式有以下几个缺点:
-
增加代码复杂性: 建造者模式引入了额外的类和方法,可能会增加代码的复杂性,特别是在对象的构建过程相对简单的情况下。在某些情况下,这种复杂性可能会过多。
-
可能会有多个建造者类: 如果要构建多种不同类型的对象,可能需要多个具体建造者类,这会增加代码的复杂性。然而,这也提供了更多的灵活性和可重用性。
-
性能开销: 建造者模式在构建对象时需要额外的方法调用,这可能在一些性能敏感的应用中引入微小的性能开销。但在大多数应用中,这个开销是可以接受的。
使用场景
通过了解了上面建造者模式的优缺点之后,可以得知,在以下的场景中可以使用建造者模式:
- 创建复杂对象: 当需要创建具有多个组成部分或属性的复杂对象时,建造者模式非常有用。这些对象的构建过程可能涉及多个步骤,并且具有复杂的依赖关系。
- 避免构造函数参数过多: 如果一个对象具有大量属性,使用构造函数传递所有这些参数可能会导致构造函数的参数列表变得非常长,难以理解和维护。建造者模式通过将属性设置过程拆分为多个方法来解决这个问题。
- 配置对象: 当对象具有多个可选配置选项时,建造者模式可以使你轻松配置对象,只需调用需要的方法来设置属性,并忽略不需要的属性。这有助于避免在构造函数中传递大量的null值或默认值。
- 创建不同表示: 建造者模式允许你使用相同的构建过程来创建不同表示。例如,可以使用相同的建造者类构建不同外观或配置的用户界面组件。
- 构建对象的顺序不重要: 如果对象的属性之间的相对顺序不重要,那么建造者模式可以提供一种更加灵活的构建方式,客户端代码可以根据需要选择构建步骤。
- 创建不可变对象: 建造者模式常用于创建不可变对象,因为一旦对象被构建,它的属性通常不可修改。这有助于确保对象的状态不会在使用过程中被意外更改。
- 复杂对象的变种: 当需要构建多个类似但略有不同的对象时,可以使用建造者模式为每个变种创建一个具体建造者,以确保构建过程的一致性。
扩展
其实除了上面的方法能够实现建造者模式以外,还可以采用内部类的方式来进行实现
java
class Car {
private final String make;
private final String model;
private final int year;
private final boolean isElectric;
private final boolean hasSunroof;
private Car(Builder builder) {
this.make = builder.make;
this.model = builder.model;
this.year = builder.year;
this.isElectric = builder.isElectric;
this.hasSunroof = builder.hasSunroof;
}
public static class Builder {
private String make;
private String model;
private int year;
private boolean isElectric;
private boolean hasSunroof;
public Builder make(String make) {
this.make = make;
return this;
}
public Builder model(String model) {
this.model = model;
return this;
}
public Builder year(int year) {
this.year = year;
return this;
}
public Builder electric(boolean isElectric) {
this.isElectric = isElectric;
return this;
}
public Builder sunroof(boolean hasSunroof) {
this.hasSunroof = hasSunroof;
return this;
}
// 使用构建者创建Car对象
public Car build() {
return new Car(this);
}
}
@Override
public String toString() {
return "Car{" +
"make='" + make + '\'' +
", model='" + model + '\'' +
", year=" + year +
", isElectric=" + isElectric +
", hasSunroof=" + hasSunroof +
'}';
}
}
上面的代码将建造者类声明为了Car产品类的内部类,并将自己的构造器私有化,只能通过Builder建造器的构建方法构建Car类,具体的使用方法的代码如下
java
public class Customer {
public static void main(String[] args) {
Car car = new Car.Builder()
.make("Toyota")
.model("Camry")
.year(2022)
.electric(false)
.sunroof(true)
.build();
System.out.println(car);
}
}
这样实现的好处是,在原来的建造者的结构上,使得代码变得更加的简洁,但是功能并没有改变,并且构建对象的顺序的权限也从指导者那里转移到用户那里。实现链式编程,提高了开发效率。