一天一个设计模式(五):建造者模式,构建优美的Java对象
建造者模式(Builder Pattern)是一种创建型设计模式,其主要作用是将一个复杂对象的构建过程与它的表现分离开。这种分离意味着同样的构建过程可以创建不同的表现,同时,不同的构建过程也可以用来创建相同的表现。这使得代码更加灵活、可维护和可扩展。
一、原理
建造者模式关注的是如何构建复杂对象,它将复杂对象的构建过程分解为多个简单对象的构建步骤,从而达到分步构建复杂对象的目的。通常情况下,这些简单对象的构建顺序是固定的,但由于使用了建造者模式,所以可以采用不同的顺序来构建出不同的对象。
建造者模式中包含以下角色:
- Builder (抽象建造者):定义了构建复杂对象所需要的各个部件的抽象接口。
- ConcreteBuilder (具体建造者):实现了Builder接口,实现各个部件的具体构建与装配方法。
- Product (产品角色):具体构建出来的复杂对象,由多个简单对象组成。
- Director (指挥者):调用具体建造者来创建复杂对象的各个部分,并按照特定的顺序装配这些部分,最终构建完成整个复杂对象。
二、示例代码
假设我们要构建一个简单的计算机,计算机由 CPU、内存、硬盘和显示器等组成。为了使用建造者模式,我们必须将计算机的构建过程分解为多个步骤,并且使用 Builder 接口定义这些步骤的规范。具体步骤如下:
1、创建抽象建造者接口 Builder,其中定义了创建 CPU、内存、硬盘和显示器等组件的方法。
public interface Builder {
void buildCPU(String cpu);
void buildMemory(String memory);
void buildHardDisk(String hardDisk);
void buildDisplay(String display);
}
2、创建具体建造者类 ConcreteBuilder,它实现了 Builder 接口,并在其中对每个组件的属性进行设置。
public class ConcreteBuilder implements Builder {
private Computer computer = new Computer();
public void buildCPU(String cpu) {
computer.setCPU(cpu);
}
public void buildMemory(String memory) {
computer.setMemory(memory);
}
public void buildHardDisk(String hardDisk) {
computer.setHardDisk(hardDisk);
}
public void buildDisplay(String display) {
computer.setDisplay(display);
}
public Computer getResult() {
return computer;
}
}
3、创建指挥者类 Director,它负责控制计算机的构建过程,并使用 Builder 接口定义的方法来设置每个组件的属性。
public class Director {
public void construct(Builder builder) {
builder.buildCPU("i7");
builder.buildMemory("16GB");
builder.buildHardDisk("1TB");
builder.buildDisplay("21 inch");
}
}
4、创建产品类 Computer,这里我们只需要提供一些简单的 getter 和 setter 方法即可。
public class Computer {
private String cpu;
private String memory;
private String hardDisk;
private String display;
public String getCPU() {
return cpu;
}
public void setCPU(String cpu) {
this.cpu = cpu;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
public String getHardDisk() {
return hardDisk;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
}
最终,我们可以通过如下代码使用建造者模式创建一个计算机对象:
public static void main(String[] args) {
Director director = new Director();
ConcreteBuilder builder = new ConcreteBuilder();
director.construct(builder);
Computer computer = builder.getResult();
}
当我们需要构建多种类型的计算机时,只需要编写不同的具体建造者实现即可。通过使用建造者模式,我们将复杂对象的构建过程和表示分离开来,使得代码更加清晰、易于维护,同时也能够提高代码的可扩展性和灵活性。
三、建造者模式的优缺点
建造者模式具有以下优点:
优点:
1、将对象的创建过程封装起来,使得客户端代码与对象的创建过程分离,从而提高系统的可维护性、可扩展性;
2、可以控制对象的创建过程,使得对象的构建过程更加灵活,可以根据不同的需求创建不同的对象;
3、可以通过建造者模式创建一些复杂的对象,同时又避免了大量重复的代码,提高了代码复用性。
缺点:
建造者模式需要定义多个角色,包括具体产品、抽象建造者、具体建造者等,因此会增加系统的复杂度;
四、SpringBoot中的建造者模式
SpringBoot 中的许多配置都用到了建造者模式,最常见的就是使用 Builder 配置类来构建 DataSource 对象。
在 SpringBoot 中,我们有时会使用 Spring 提供的 org.springframework.boot.jdbc.DataSourceBuilder 类来构建一个数据源对象。 DataSourceBuilder 中提供了许多静态方法用于创建不同类型的数据源,例如:create()、create(ClassLoader classLoader)、create(ClassLoader classLoader, Environment environment) 等等。这些静态方法都返回了一个 DataSourceBuilder 类型的对象,在这个对象的基础上,我们又可以继续调用其它的方法来设置数据源的各种属性。
举例来说,如果要使用 Hikari 数据源,则可以通过如下代码来创建一个 HikariDataSource 对象:
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.jdbc.DataSourceBuilder;
public class DataSourceExample {
public static void main(String[] args) {
HikariDataSource dataSource = DataSourceBuilder.create()
.type(HikariDataSource.class)
.url("jdbc:h2:mem:test")
.username("sa")
.password("")
.build();
}
}
在上述代码中,我们首先通过 DataSourceBuilder.create() 静态方法创建一个数据源的 Builder 对象,并且使用 type(HikariDataSource.class) 方法指定了数据源类型为 HikariDataSource。然后,我们又可以继续使用其它的方法来设置数据源的各种属性,例如 url()、username()、password() 等等,最终通过 build() 方法来构建出一个数据源对象。
在这个过程中,我们可以看到 DataSourceBuilder 中使用了建造者模式来构建一个数据源对象。具体来说,我们可以将 Builder 视为一个抽象建造者,DataSourceBuilder 类则是其具体建造者的实现,HikariDataSource 则是最终构建出来的产品。通过这种方式,我们可以将创建产品的过程和表示分离开来,使得代码更加清晰、易于维护,同时也能够提高代码的可扩展性和灵活性。
以下是 DataSourceBuilder 部分源代码解释:
public final class DataSourceBuilder<T extends DataSource> {
// 支持的数据源类型数组
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
"com.zaxxer.hikari.HikariDataSource",
"org.apache.tomcat.jdbc.pool.DataSource",
"org.apache.commons.dbcp2.BasicDataSource"
};
// 数据源类型
private Class<? extends DataSource> type;
// 类加载器
private ClassLoader classLoader;
// 属性键值对
private Map<String, String> properties = new HashMap<>();
/**
* 静态工厂方法,创建一个默认的 DataSourceBuilder 对象
*/
public static DataSourceBuilder<?> create() {
return new DataSourceBuilder<>(null);
}
/**
* 静态工厂方法,创建一个指定类加载器的 DataSourceBuilder 对象
*/
public static DataSourceBuilder<?> create(ClassLoader classLoader) {
return new DataSourceBuilder<>(classLoader);
}
/**
* 私有构造函数,创建一个指定类加载器的 DataSourceBuilder 对象
*/
private DataSourceBuilder(ClassLoader classLoader) {
this.classLoader = classLoader;
}
......
/**
* 构建数据源对象
*/
@SuppressWarnings("unchecked")
public T build() {
Class<? extends DataSource> type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return (T) result;
}
/**
* 设置数据源类型
* @param type 数据源类型
*/
@SuppressWarnings("unchecked")
public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) {
this.type = type;
return (DataSourceBuilder<D>) this;
}
/**
* 设置数据源 URL
* @param url 数据源 URL
*/
public DataSourceBuilder<T> url(String url) {
this.properties.put("url", url);
return this;
}
/**
* 设置数据源驱动类名
* @param driverClassName 数据源驱动类名
*/
public DataSourceBuilder<T> driverClassName(String driverClassName) {
this.properties.put("driverClassName", driverClassName);
return this;
}
/**
* 设置数据源用户名
* @param username 数据源用户名
*/
public DataSourceBuilder<T> username(String username) {
this.properties.put("username", username);
return this;
}
/**
* 设置数据源密码
* @param password 数据源密码
*/
public DataSourceBuilder<T> password(String password) {
this.properties.put("password", password);
return this;
}
......
}