Java设计模式中的建造者(Builder)模式解读
作者:蜡笔小久
为什么需要建造者模式
在我们日常的开发中,一般使用new 关键字通过构造器就可以实现对象的创建,然后通过set来实现成员变量的修改, 为什么在Java中还需要建造者模式来创建对象?
假如我们系统中,需要有一个统一的日志模块,包含了日志内容(content)、日志所属组织(orgId)、日志来源(logSource: 如app, 平台操作)、用户名(username)。
通常情况下我们可以直接通过定义一个日志类, 然后通过构造器的方式, 进行参数设置。
public class LogEntity {
@Getter
private Long id;
/**
* 操作时间
*/
@Getter
private String eventDate;
/**
* 操作日志模块: default other
*/
@Getter
private String module;
/**
* 操作内容
*/
@Getter
private String message;
/**
* IP
*/
@Getter
private String ipAddress;
/**
* 操作用户
*/
@Getter
private String username;
/**
* 日志所属组织
*/
@Getter
private Long organizationId;
/**
* 日志来源('日志来源:1:终端上报,2:平台下发,3:平台操作,4:APP操作')
*/
@Getter
private Integer logSource;
public PlatformLoggerEntity(Long id, String module, String message, Long organizationId, Integer logSource) {
this.id = id;
this.module = module;
this.message = message;
this.organizationId = organizationId;
this.logSource = logSource;
}
}现在日志类,只有5个参数,参数的个数还在可接受范围内,假如后续LogEntity类要进行扩展, 存储的参数变成10个或则更多, 构造器就会变得很长,并且可读性和易用性都很变得很差。在使用构造器时,还需要记得每个位置传递的是什么字段,如果字段类型相同,还有可能出现传递错误的情况。
你可能会说,我们可以通过set方法来设置值,确实,通过set方法来设置值可以避免必填字段和非必填字段问题。 代码的可用性也提高了。
出现的问题
1.id作为必填字段,需要写在构造器中,如果必填字段过多,又会出现构造器参数列表过长的问题
2. 字段间存在关联关系,比如logSource是平台,则需要设置用户的所属企业id和用户名, 如果我们通过set来设置值,那我们校验的逻辑就没有地方放了
3. 如果我们想把LogEntity设置为不可变对象,对象初始化后就不允许改变,那么我们再暴露set方法,就达到到该效果
因此我们可以通过建造者模式重写方面的类, 重写好的类如下:
public class PlatformLoggerEntity {
/**
* 使用雪花算法生成ID
*/
@Getter
private Long id;
/**
* 操作时间
*/
@Getter
private String eventDate;
/**
* 操作日志模块: default other
*/
@Getter
private String module;
/**
* 操作内容
*/
@Getter
private String message;
/**
* IP
*/
@Getter
private String ipAddress;
/**
* 操作用户
*/
@Getter
private String username;
/**
* 日志所属组织
*/
@Getter
private Long organizationId;
/**
* 日志来源('日志来源:1:终端上报,2:平台下发,3:平台操作,4:APP操作')
*/
@Getter
private Integer logSource;
/**
* 显示类型 0:正常显示 1:超链显示
*/
@Getter
private Integer showType;
private PlatformLoggerEntity(Builder builder) {
this.id = IdUtil.next();
this.username = SystemHelper.getCurrentUserName();
this.ipAddress = SystemHelper.getIpAddress();
this.organizationId = builder.organizationId;
this.module = builder.module.getModule();
this.message = builder.message;
this.monitoringOperation = builder.monitoringOperation;
this.monitorName = builder.monitorName;
this.plateColor = builder.plateColor;
this.showType = builder.showType;
this.eventDate = LocalDateUtils.dateTimeFormat(new Date());
this.logSource = builder.logSource.getLoggerSource();
}
/**
* @return 创建builder类的实例对象
*/
public static Builder newBuilder() {
return new Builder();
}
/**
* 日誌Builder类
*/
public static class Builder {
/**
* 操作日志模块: default other
*/
private LoggerModule module;
/**
* 操作内容
*/
private String message;
/**
* 日志所属组织
*/
private Long organizationId;
/**
* 日志来源('日志来源:1:终端上报,2:平台下发,3:平台操作,4:APP操作')
*/
private LoggerSource logSource;
/**
* 显示类型 0:正常显示 1:超链显示
*/
private Integer showType;
publick LogEntity build() {
// 默认为平台操作
if (Objects.isNull(logSource)) {
this.logSource = LoggerSource.PLATFORM_OPERATION;
}
if (Objects.isNull(organizationId)) {
this.organizationId = SystemHelper.getCurrentUserOrgId();
}
// 用于前端展示
if (Objects.isNull(module)) {
this.module = LoggerModule.DEFAULT;
this.showType = LoggerConstant.SHOW_TYPE_NORMAL;
} else if (module.equals(LoggerModule.DEFAULT)) {
this.showType = LoggerConstant.SHOW_TYPE_NORMAL;
} else {
this.showType = LoggerConstant.SHOW_TYPE_HYPERLINK;
}
return new PlatformLoggerEntity(this);
}
public Builder module(LoggerModule module) {
this.module = module;
return this;
}
public Builder message(String message) {
this.message = message;
return this;
}
public Builder organizationId(Long organizationId) {
this.organizationId = organizationId;
return this;
}
public Builder logSource(LoggerSource logSource) {
this.logSource = logSource;
return this;
}
}
}我们重写LogEntity类后, 我们可以通过newBuilder来获取内部的Builder对象,并且通过build函数来创建LogEntity对象。
1.从上面代码可以看出LogEntitty所有的成员属性都只有get方法,避免出现随意修改的情况
2.通过LogEntity内部的Builder类中的build() 来增加了相关联属性的验证,避免调用则漏传或则错传参数
总结
什么情况下我们可以选择建造者(Builder)模式来创建对象, 总结了一下三点:
1.如果类中的必填字段过多,构造函数过长,生成对象时需要校验必填属性
2.如果字段之间存在关联,生成对象时进行校验。
3.我们需要创建不可变对象,不能暴露实体的set方法时
到此这篇关于Java设计模式中的建造者(Builder)模式解读的文章就介绍到这了,更多相关Java建造者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
