🌟建造者模式:复杂对象的“拼搭大师”
一、建造者模式初相识
✨你是否遇到过参数众多的对象创建问题?比如创建一个包含 CPU、内存、硬盘的电脑对象,传统方式需要写超长的构造函数。建造者模式就像 “乐高积木”,允许你分步骤组装复杂对象,最终通过build
方法一键生成完整产品!
在软件开发中,我们常常会遇到创建复杂对象的需求。这些对象可能包含多个部分,每个部分都有不同的属性和配置。例如,创建一个游戏角色,可能需要设置角色的名称、性别、职业、技能、装备等多个属性。如果使用传统的构造函数来创建这样的对象,构造函数的参数列表可能会非常长,难以维护和阅读。而且,如果我们需要创建不同类型的游戏角色,可能需要编写多个不同的构造函数,这会导致代码的重复和冗余。
建造者模式就是为了解决这类问题而诞生的。它将复杂对象的构建过程和表示过程分离,使得我们可以通过一步步的构建来创建对象,而不需要一次性提供所有的参数。这样不仅可以提高代码的可读性和可维护性,还可以方便地创建不同类型的对象。
二、核心思想与结构
2.1 模式定义
建造者模式,简单来说,就是把复杂对象的构建过程和它最终呈现出来的样子分离开。这样做有个很大的好处,就是同样一套构建步骤,能生成不同表现形式的对象。就像我们搭积木,不管是搭城堡还是搭机器人,用的积木可能是一样的,只是搭建顺序和组合方式不同,最后呈现的东西就不一样。
它的核心在于,将复杂对象的创建过程分解为一系列简单的步骤,然后通过一个指挥者来协调这些步骤的执行顺序,最终构建出完整的对象。这种方式不仅提高了代码的可读性和可维护性,还使得我们可以方便地创建不同类型的对象,而无需修改太多代码。 具体而言,它是通过以下几个关键步骤来实现的:
分步构建:将复杂对象的构建过程分解为多个简单的步骤,每个步骤负责构建对象的一个部分。
统一组装:通过一个指挥者来协调各个步骤的执行顺序,将各个部分组装成完整的对象。
解耦构建逻辑与产品表示:使得构建过程和产品的表示方式相互独立,从而可以灵活地改变构建过程或产品的表示方式,而不会影响到对方。
我们可以把建造者模式类比成定制家具的过程。想象你要定制一张桌子,你不需要一下子告诉工匠所有的细节,而是可以一步一步来。先选择桌子的材质,比如是实木还是人造板;再确定尺寸,是多大的桌面和多高的桌腿;然后挑选颜色,是深色还是浅色。工匠根据你的这些选择,按照一定的流程把桌子组装起来,最后交给你一张符合你要求的桌子。这里,选择材质、尺寸、颜色的过程就是分步构建,工匠把这些部件组装成桌子的过程就是统一组装,而你最终得到的桌子就是产品。通过这种方式,你可以轻松定制出不同风格、不同尺寸的桌子,而不需要每次都重新设计整个制作流程。
2.2 四大核心角色
在建造者模式中,有四个核心角色,它们相互协作,共同完成复杂对象的构建。这四个角色分别是:产品(Product)、抽象建造者(Builder)、具体建造者(ConcreteBuilder)和指挥者(Director)。下面我们通过一个创建电脑的例子来详细了解一下这四个角色:
角色 | 作用 | 案例中的体现 |
---|---|---|
产品(Product) | 最终构建的复杂对象 | 电脑、用户配置 |
抽象建造者(Builder) | 定义构建步骤接口 | 电脑组装指南 |
具体建造者(ConcreteBuilder) | 实现具体构建逻辑 | 游戏本建造者、商务本建造者 |
指挥者(Director) | 控制构建流程 | 组装工程师 |
产品(Product):它是我们最终要构建的复杂对象,包含多个组成部分。就像电脑,它由 CPU、内存、硬盘、显卡等多个部件组成。在代码中,产品通常是一个具体的类,包含了各个部件的属性和相关的操作方法。
// 电脑类,作为产品角色
public class Computer {
private String cpu;
private String memory;
private String hardDisk;
// 其他部件属性...
// 构造函数、Getter和Setter方法
public Computer(String cpu, String memory, String hardDisk) {
this.cpu = cpu;
this.memory = memory;
this.hardDisk = hardDisk;
}
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 void showConfig() {
System.out.println("电脑配置:");
System.out.println("CPU: " + cpu);
System.out.println("内存: " + memory);
System.out.println("硬盘: " + hardDisk);
// 其他部件展示...
}
}
抽象建造者(Builder):它定义了构建产品各个部件的接口,通常是一个抽象类或接口。它不涉及具体的构建逻辑,只是规定了构建产品所需的方法。就好比电脑组装指南,它告诉我们组装电脑需要进行哪些步骤,但不涉及具体如何操作。在代码中,抽象建造者通常定义了一系列抽象方法,每个方法对应构建产品的一个部件。
// 抽象建造者接口
public interface ComputerBuilder {
void buildCPU();
void buildMemory();
void buildHardDisk();
// 其他构建部件的方法...
Computer getComputer();
}
具体建造者(ConcreteBuilder):它实现了抽象建造者定义的接口,负责具体的构建逻辑。不同的具体建造者可以构建出不同类型的产品。比如游戏本建造者和商务本建造者,它们根据不同的需求,选择不同的部件来组装电脑。在代码中,具体建造者类实现了抽象建造者接口的所有方法,完成产品各个部件的具体构建和装配。
// 游戏本建造者,具体建造者角色
public class GamingComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer("", "", "");
@Override
public void buildCPU() {
computer.setCpu("高性能CPU,如Intel Core i9");
}
@Override
public void buildMemory() {
computer.setMemory("大容量内存,如32GB DDR4");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("高速固态硬盘,如1TB NVMe SSD");
}
// 其他部件构建方法...
@Override
public Computer getComputer() {
return computer;
}
}
// 商务本建造者,具体建造者角色
public class BusinessComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer("", "", "");
@Override
public void buildCPU() {
computer.setCpu("低功耗CPU,如Intel Core i5");
}
@Override
public void buildMemory() {
computer.setMemory("适中内存,如16GB DDR4");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("普通固态硬盘,如512GB SSD");
}
// 其他部件构建方法...
@Override
public Computer getComputer() {
return computer;
}
}
指挥者(Director):它负责控制构建流程,按照一定的顺序调用具体建造者的方法来构建产品。就像组装工程师,他根据客户的需求,指挥工人按照特定的顺序组装电脑。在代码中,指挥者类通常包含一个具体建造者的引用,并在其构建方法中调用具体建造者的各个构建方法。
// 指挥者类
public class ComputerDirector {
private ComputerBuilder computerBuilder;
public ComputerDirector(ComputerBuilder computerBuilder) {
this.computerBuilder = computerBuilder;
}
public Computer constructComputer() {
computerBuilder.buildCPU();
computerBuilder.buildMemory();
computerBuilder.buildHardDisk();
// 其他部件构建调用...
return computerBuilder.getComputer();
}
}
通过这四个角色的协同工作,我们可以轻松地创建出不同类型的电脑,而不需要在客户端代码中直接处理复杂的构建逻辑。这种方式不仅提高了代码的可维护性和可扩展性,还使得代码更加清晰和易于理解。
三、Java 代码实战:电脑组装案例
3.1 传统方式的痛点
在实际开发中,如果不使用建造者模式,创建复杂对象可能会遇到诸多问题。比如,在创建电脑对象时,如果使用传统的构造函数,可能会出现以下代码:
public class TraditionalComputer {
private String cpu;
private String memory;
private String hardDisk;
// 其他部件属性...
// 构造函数,参数众多
public TraditionalComputer(String cpu, String memory, String hardDisk, /* 其他部件参数 */) {
this.cpu = cpu;
this.memory = memory;
this.hardDisk = hardDisk;
// 其他部件初始化...
}
}
当需要创建不同配置的电脑时,可能需要编写多个不同参数组合的构造函数,这会导致代码冗余且难以维护。而且,如果电脑的部件增加或减少,构造函数的参数列表也需要相应地修改,这会增加出错的风险。
3.2 建造者模式实现
接下来,我们使用建造者模式来实现电脑组装的功能。通过建造者模式,我们可以将电脑的构建过程分解为多个步骤,每个步骤负责构建一个部件,然后通过指挥者来协调这些步骤的执行顺序,最终构建出完整的电脑对象。
首先,定义电脑类(产品):
// 电脑类,作为产品角色
public class Computer {
private String cpu;
private String memory;
private String hardDisk;
// 其他部件属性...
// 构造函数、Getter和Setter方法
public Computer(String cpu, String memory, String hardDisk) {
this.cpu = cpu;
this.memory = memory;
this.hardDisk = hardDisk;
}
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 void showConfig() {
System.out.println("电脑配置:");
System.out.println("CPU: " + cpu);
System.out.println("内存: " + memory);
System.out.println("硬盘: " + hardDisk);
// 其他部件展示...
}
}
然后,定义抽象建造者接口:
// 抽象建造者接口
public interface ComputerBuilder {
void buildCPU();
void buildMemory();
void buildHardDisk();
// 其他构建部件的方法...
Computer getComputer();
}
接着,实现具体建造者类,以游戏本建造者为例:
// 游戏本建造者,具体建造者角色
public class GamingComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer("", "", "");
@Override
public void buildCPU() {
computer.setCpu("高性能CPU,如Intel Core i9");
}
@Override
public void buildMemory() {
computer.setMemory("大容量内存,如32GB DDR4");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("高速固态硬盘,如1TB NVMe SSD");
}
// 其他部件构建方法...
@Override
public Computer getComputer() {
return computer;
}
}
再定义指挥者类:
// 指挥者类
public class ComputerDirector {
private ComputerBuilder computerBuilder;
public ComputerDirector(ComputerBuilder computerBuilder) {
this.computerBuilder = computerBuilder;
}
public Computer constructComputer() {
computerBuilder.buildCPU();
computerBuilder.buildMemory();
computerBuilder.buildHardDisk();
// 其他部件构建调用...
return computerBuilder.getComputer();
}
}
最后,在客户端代码中使用建造者模式创建电脑对象:
public class Client {
public static void main(String[] args) {
// 创建游戏本建造者
ComputerBuilder gamingBuilder = new GamingComputerBuilder();
// 创建指挥者并传入游戏本建造者
ComputerDirector director = new ComputerDirector(gamingBuilder);
// 构建游戏本
Computer gamingComputer = director.constructComputer();
// 展示游戏本配置
gamingComputer.showConfig();
}
}
运行上述代码,你将看到游戏本的配置信息被打印出来。通过建造者模式,我们可以轻松地创建不同类型的电脑,并且代码结构更加清晰,易于维护和扩展。
四、应用场景大揭秘
建造者模式在软件开发中有着广泛的应用场景,下面我们来看看它在实际项目中的一些常见应用。
4.1 复杂对象构建
在构建复杂对象时,建造者模式可以将对象的构建过程分解为多个简单的步骤,使得代码更加清晰和易于维护。比如在游戏开发中,创建一个游戏角色可能需要设置角色的名称、性别、职业、技能、装备等多个属性。使用建造者模式,我们可以将这些属性的设置过程封装在具体建造者类中,通过指挥者来协调这些步骤的执行顺序,从而轻松地创建出不同类型的游戏角色。
在数据库操作中,构建复杂的 SQL 查询语句也可以使用建造者模式。例如,当我们需要根据不同的条件查询数据库时,查询语句可能会非常复杂,包含多个表的连接、筛选条件、排序等。使用建造者模式,我们可以将这些条件的构建过程封装在具体建造者类中,通过指挥者来协调这些步骤的执行顺序,从而生成正确的 SQL 查询语句。 下面是一个使用建造者模式构建 SQL 查询语句的示例:
// SQL查询语句的产品类
public class SQLQuery {
private String selectClause;
private String fromClause;
private String whereClause;
private String orderByClause;
// Getter和Setter方法
public String getSelectClause() {
return selectClause;
}
public void setSelectClause(String selectClause) {
this.selectClause = selectClause;
}
public String getFromClause() {
return fromClause;
}
public void setFromClause(String fromClause) {
this.fromClause = fromClause;
}
public String getWhereClause() {
return whereClause;
}
public void setWhereClause(String whereClause) {
this.whereClause = whereClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
// 生成完整的SQL查询语句
public String generateQuery() {
StringBuilder query = new StringBuilder("SELECT ");
query.append(selectClause);
query.append(" FROM ");
query.append(fromClause);
if (whereClause != null && !whereClause.isEmpty()) {
query.append(" WHERE ");
query.append(whereClause);
}
if (orderByClause != null && !orderByClause.isEmpty()) {
query.append(" ORDER BY ");
query.append(orderByClause);
}
return query.toString();
}
}
// 抽象建造者接口
public interface SQLQueryBuilder {
void buildSelectClause();
void buildFromClause();
void buildWhereClause();
void buildOrderByClause();
SQLQuery getSQLQuery();
}
// 具体建造者类
public class UserQueryBuilder implements SQLQueryBuilder {
private SQLQuery sqlQuery = new SQLQuery();
@Override
public void buildSelectClause() {
sqlQuery.setSelectClause("*");
}
@Override
public void buildFromClause() {
sqlQuery.setFromClause("users");
}
@Override
public void buildWhereClause() {
sqlQuery.setWhereClause("age > 20");
}
@Override
public void buildOrderByClause() {
sqlQuery.setOrderByClause("name ASC");
}
@Override
public SQLQuery getSQLQuery() {
return sqlQuery;
}
}
// 指挥者类
public class SQLQueryDirector {
private SQLQueryBuilder sqlQueryBuilder;
public SQLQueryDirector(SQLQueryBuilder sqlQueryBuilder) {
this.sqlQueryBuilder = sqlQueryBuilder;
}
public SQLQuery constructQuery() {
sqlQueryBuilder.buildSelectClause();
sqlQueryBuilder.buildFromClause();
sqlQueryBuilder.buildWhereClause();
sqlQueryBuilder.buildOrderByClause();
return sqlQueryBuilder.getSQLQuery();
}
}
在客户端代码中使用建造者模式构建 SQL 查询语句:
public class Client {
public static void main(String[] args) {
SQLQueryBuilder userQueryBuilder = new UserQueryBuilder();
SQLQueryDirector director = new SQLQueryDirector(userQueryBuilder);
SQLQuery query = director.constructQuery();
System.out.println(query.generateQuery());
}
}
4.2 参数配置管理
在处理参数配置时,建造者模式可以帮助我们更好地管理和维护配置信息。例如,在一个微服务架构中,每个微服务可能都有自己的配置文件,这些配置文件包含了各种参数,如数据库连接信息、服务端口、日志级别等。使用建造者模式,我们可以将这些参数的配置过程封装在具体建造者类中,通过指挥者来协调这些步骤的执行顺序,从而生成正确的配置对象。
在开发 APP 时,应用主题的设置也可以使用建造者模式。不同的用户可能喜欢不同的主题风格,如亮色主题、暗色主题、自定义主题等。使用建造者模式,我们可以将主题的各个元素(如颜色、字体、图标等)的设置过程封装在具体建造者类中,通过指挥者来协调这些步骤的执行顺序,从而生成不同风格的主题对象。
4.3 分步骤操作
当一个操作需要分多个步骤完成时,建造者模式可以将这些步骤封装在具体建造者类中,通过指挥者来协调这些步骤的执行顺序,从而确保操作的顺利进行。例如,在一个电商系统中,订单支付流程通常包括选择支付方式、输入支付密码、确认支付等多个步骤。使用建造者模式,我们可以将这些步骤的处理逻辑封装在具体建造者类中,通过指挥者来协调这些步骤的执行顺序,从而实现订单的支付功能。
在生成报表时,也可以使用建造者模式。报表的生成通常包括查询数据、格式化数据、绘制图表等多个步骤。使用建造者模式,我们可以将这些步骤的处理逻辑封装在具体建造者类中,通过指挥者来协调这些步骤的执行顺序,从而生成符合要求的报表。
五、优缺点大 PK
任何设计模式都有其优缺点,建造者模式也不例外。下面我们来分析一下建造者模式的优缺点,以便在实际应用中能够更好地权衡利弊。
5.1 优点
解耦构建与表示:将复杂对象的构建过程和它的表示分离,使得代码的职责更加单一,也提高了代码的可维护性。例如在电脑组装案例中,电脑的构建过程(选择 CPU、内存、硬盘等部件)和最终的电脑产品表示(完整的电脑对象)是分开的,这样如果需要修改电脑的配置或者添加新的部件,只需要修改建造者类,而不会影响到电脑类。
提高灵活性:可以方便地创建不同类型的产品,只需要实现不同的具体建造者类即可。比如我们可以创建游戏本建造者、商务本建造者、工作站建造者等,它们都实现了电脑建造者接口,但构建出来的电脑配置不同,以满足不同用户的需求。
便于控制构建过程:通过指挥者可以精确地控制复杂对象的构建过程,按照特定的顺序调用建造者的方法。例如在订单支付流程中,指挥者可以确保先选择支付方式,再输入支付密码,最后确认支付,这样可以保证支付流程的正确性和安全性。
代码复用性高:建造者模式中,建造者类和产品类可以被多个地方复用。例如,不同的游戏角色创建可能都需要用到相同的属性设置方法,这些方法可以封装在建造者类中,被不同的游戏角色创建逻辑复用。
5.2 缺点
类的数量增加:为了实现建造者模式,需要定义多个类,包括产品类、抽象建造者类、具体建造者类和指挥者类,这会增加项目的类数量和复杂性。如果项目规模较小,使用建造者模式可能会显得过于繁琐。
不适用于简单对象:对于简单对象的创建,使用建造者模式可能会显得过于复杂,因为简单对象不需要复杂的构建过程,直接使用构造函数创建即可。例如创建一个简单的整数对象,使用建造者模式就会增加不必要的代码量。
维护成本:如果产品的结构发生变化,可能需要修改抽象建造者类、具体建造者类和指挥者类的代码,这会增加维护成本。例如,如果电脑的部件发生变化,如增加了一个新的部件或者修改了某个部件的构建方式,就需要相应地修改电脑建造者接口、具体建造者类和指挥者类的代码。
六、避坑指南
在使用建造者模式时,有一些常见的问题和注意事项需要我们关注,以避免在开发过程中出现不必要的麻烦。
6.1 必选参数校验
在建造者模式中,通常会在build
方法中对必填参数进行校验,以确保创建的对象是有效的。例如,在创建用户对象时,用户名和密码可能是必填项,我们可以在build
方法中检查这些必填项是否为空。如果必填项为空,应该抛出异常,提示用户缺少必要的参数。这样可以避免创建出不完整或无效的对象,从而保证系统的稳定性和可靠性。
public class User {
private String username;
private String password;
// 其他属性...
// 私有构造函数,只能通过建造者创建对象
private User(Builder builder) {
this.username = builder.username;
this.password = builder.password;
// 其他属性赋值...
}
// 建造者类
public static class Builder {
private String username;
private String password;
// 其他属性...
public Builder setUsername(String username) {
this.username = username;
return this;
}
public Builder setPassword(String password) {
this.password = password;
return this;
}
// 其他设置方法...
public User build() {
if (username == null || username.isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (password == null || password.isEmpty()) {
throw new IllegalArgumentException("密码不能为空");
}
return new User(this);
}
}
}
6.2 不可变性
如果希望创建的对象是不可变的,即创建后其属性不能被修改,那么可以将产品类的属性声明为final
,并且不提供修改属性的方法。这样可以保证对象的状态在创建后不会被意外改变,从而提高代码的安全性和可维护性。例如,在创建一个配置对象时,我们可能希望配置一旦确定就不再更改,这时就可以使用final
关键字来修饰配置对象的属性。
public class Configuration {
private final String key;
private final String value;
// 构造函数
public Configuration(Builder builder) {
this.key = builder.key;
this.value = builder.value;
}
// 建造者类
public static class Builder {
private String key;
private String value;
public Builder setKey(String key) {
this.key = key;
return this;
}
public Builder setValue(String value) {
this.value = value;
return this;
}
public Configuration build() {
return new Configuration(this);
}
}
}
6.3 链式调用规范
在建造者模式中,为了实现链式调用,通常会让每个设置属性的方法返回this
,这样可以方便地在一行代码中设置多个属性。但是需要注意的是,返回的this
对象应该是当前具体建造者的实例,而不是抽象建造者的实例,否则可能会导致类型错误。例如,在游戏本建造者中,每个设置部件的方法都应该返回this
,以支持链式调用。
public class GamingComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer("", "", "");
@Override
public GamingComputerBuilder buildCPU() {
computer.setCpu("高性能CPU,如Intel Core i9");
return this;
}
@Override
public GamingComputerBuilder buildMemory() {
computer.setMemory("大容量内存,如32GB DDR4");
return this;
}
@Override
public GamingComputerBuilder buildHardDisk() {
computer.setHardDisk("高速固态硬盘,如1TB NVMe SSD");
return this;
}
// 其他部件构建方法...
@Override
public Computer getComputer() {
return computer;
}
}
6.4 组合其他模式
建造者模式可以与其他设计模式结合使用,以满足更复杂的需求。例如,可以与工厂模式结合,使用工厂模式来创建建造者实例,这样可以更好地管理建造者的创建和使用。也可以与单例模式结合,将指挥者或建造者设计为单例,以确保在整个应用程序中只有一个实例。通过组合其他模式,可以充分发挥各种模式的优势,提高代码的灵活性和可扩展性。
// 工厂类,用于创建建造者实例
public class BuilderFactory {
public static ComputerBuilder createBuilder(String type) {
switch (type) {
case "gaming":
return new GamingComputerBuilder();
case "business":
return new BusinessComputerBuilder();
default:
throw new IllegalArgumentException("不支持的类型: " + type);
}
}
}
七、模式对比
在创建型模式的大家庭中,工厂方法模式、建造者模式和原型模式各有所长。下面通过表格对比一下这三种模式的核心差异和适用场景。
模式 | 核心差异 | 适用场景 |
---|---|---|
工厂方法 | 统一创建对象,将对象的创建和使用分离,使得客户端不需要关心对象的具体创建过程,通过工厂类来创建对象 | 简单对象创建,当需要创建的对象种类较少,且创建过程相对简单时,使用工厂方法模式可以将对象的创建逻辑封装在工厂类中,提高代码的可维护性和可扩展性。例如创建不同品牌的手机对象,每个手机品牌的创建过程可能只是简单地实例化一个类并设置一些基本属性,这时使用工厂方法模式就很合适。 |
建造者 | 分步构建复杂对象,将复杂对象的构建过程和表示过程分离,通过一步步构建对象的各个部分来创建复杂对象 | 参数众多的对象,当对象的创建需要设置大量的参数,且这些参数之间存在一定的依赖关系或创建步骤较为复杂时,建造者模式可以将这些复杂的构建过程封装在具体建造者类中,通过指挥者来协调各个步骤的执行顺序,使得代码更加清晰和易于维护。例如创建一个游戏角色,需要设置角色的名称、性别、职业、技能、装备等多个属性,且这些属性之间可能存在相互影响,使用建造者模式就可以很好地管理这些属性的设置过程。 |
原型 | 复制现有对象,通过克隆现有对象来创建新对象,避免了复杂的初始化过程 | 快速生成相似对象,当创建新对象的成本较高,或者需要创建大量相似对象时,原型模式可以通过复制现有对象来快速创建新对象,提高创建效率。例如在游戏开发中,需要创建大量相同类型的怪物对象,每个怪物对象的初始属性可能相同,只是在游戏过程中会有一些动态变化,这时使用原型模式就可以通过克隆一个原型怪物对象来快速创建多个怪物对象,减少创建时间和资源消耗。 |
八、总结与实践建议
建造者模式是应对复杂对象构建场景的利器,适用于以下情况:
对象属性较多且包含可选参数:当一个对象的属性众多,且部分属性是可选的,使用建造者模式可以避免构造函数参数列表过长的问题,同时方便对可选参数进行设置和管理。
需要灵活组合不同配置:如果需要创建不同配置的对象,且这些配置之间有一定的组合关系,建造者模式可以通过不同的具体建造者类来实现灵活的配置组合。
构建过程需复杂校验:当对象的构建过程需要进行复杂的校验和逻辑处理时,建造者模式可以将这些校验和处理逻辑封装在具体建造者类中,确保创建的对象符合要求。
在实际开发中,我们可以参考以下建议来更好地运用建造者模式:
简单场景可省略 Director 角色:对于一些简单的对象构建场景,如果构建步骤比较固定,且不需要复杂的流程控制,可以省略指挥者(Director)角色,直接在具体建造者类中实现构建流程。这样可以简化代码结构,提高开发效率。
使用 Lombok 的 @Builder 注解简化代码:Lombok 的@Builder
注解可以自动生成建造者模式的相关代码,大大简化了手动编写建造者模式的工作量。通过在类上添加@Builder
注解,Lombok 会自动生成一个内部静态类作为建造者,并在原始类中添加一个私有构造函数。我们可以使用链式调用的方式设置对象的属性,并且可以在需要时进行可选属性的设置。例如:
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class Person {
private String name;
private int age;
private String address;
private String phoneNumber;
}
使用时:
Person person = Person.builder()
.name("John")
.age(30)
.address("123 Main St")
.phoneNumber("555-1234")
.build();
注意避免 Builder 类属性与 Product 类重复:在设计建造者模式时,要注意避免 Builder 类的属性与 Product 类的属性重复定义。可以将一些与构建过程相关的临时属性或中间状态放在 Builder 类中,而 Product 类只包含最终的对象属性。这样可以减少代码的冗余,提高代码的可读性和可维护性。
你在哪些项目中用过建造者模式?评论区聊聊你的 “拼搭” 经验吧!👇💡
:Java 的StringBuilder
和Stream.Builder
就是建造者模式的经典应用!