# 建造者模式(Builder Pattern)
代码地址
- Github:DesignPattern/src/main/java/com/design/builder (opens new window)
- Gitee(码云):DesignPattern/src/main/java/com/design/builder (opens new window)
# 1. 介绍
建造者模式使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的
# 1.1. 特点
待补充
# 1.2. 结构
待补充
# 1.3. 优缺点
- 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示
- 主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定
- 何时使用:一些基本部件不会变,而其组合经常变化的时候 如何解决:将变与不变分离开
- 关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系
- 使用场景:需要生成的对象具有复杂的内部结构。需要生成的对象内部属性本身相互依赖
- 优点:
- 建造者独立,易扩展
- 便于控制细节风险
- 缺点:
- 产品必须有共同点,范围有限制
- 如内部变化复杂,会有很多的建造类
- 应用实例:JAVA 中的 StringBuilder
- 注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序
# 2. 代码
常用模式,直接看使用
# 3. 使用
# 3.1. IDEA实现
使用 IDEA
插件 Builder-Generator
/**
* 应用
*
* @author wliduo[i@dolyw.com]
* @date 2022/2/11 14:13
*/
public class App {
/** 主键ID */
private Long appId;
/** 应用代码 */
private String appCode;
/** 应用名称 */
private String appName;
/** 应用私钥 */
private String appSecret;
/** 应用类型 */
private String appType;
/** 创建人 */
private String createdBy;
/** 创建时间 */
private Date createdTime;
public Long getAppId() {
return appId;
}
public void setAppId(Long appId) {
this.appId = appId;
}
public String getAppCode() {
return appCode;
}
public void setAppCode(String appCode) {
this.appCode = appCode;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public String getAppType() {
return appType;
}
public void setAppType(String appType) {
this.appType = appType;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
/**
* 建造者模式
*
* @return
*/
public static Builder Builder() {
return new Builder();
}
/**
* 建造者模式 - Builder内部类
*/
public static final class Builder {
private Long appId;
private String appCode;
private String appName;
private String appSecret;
private String appType;
private String createdBy;
private Date createdTime;
private Builder() {
}
public Builder withAppId(Long appId) {
this.appId = appId;
return this;
}
public Builder withAppCode(String appCode) {
this.appCode = appCode;
return this;
}
public Builder withAppName(String appName) {
this.appName = appName;
return this;
}
public Builder withAppSecret(String appSecret) {
this.appSecret = appSecret;
return this;
}
public Builder withAppType(String appType) {
this.appType = appType;
return this;
}
public Builder withCreatedBy(String createdBy) {
this.createdBy = createdBy;
return this;
}
public Builder withCreatedTime(Date createdTime) {
this.createdTime = createdTime;
return this;
}
public App build() {
App app = new App();
app.setAppId(appId);
app.setAppCode(appCode);
app.setAppName(appName);
app.setAppSecret(appSecret);
app.setAppType(appType);
app.setCreatedBy(createdBy);
app.setCreatedTime(createdTime);
return app;
}
}
}
/**
* 建造者模式
*
* @author wliduo[i@dolyw.com]
* @date 2022/2/11 14:27
*/
public class Main {
public static void main(String[] args) {
App app = App.Builder()
.withAppId(1L)
.withAppCode("App")
.withAppName("XX系统")
.withCreatedBy("XXX")
.build();
Channel channel = Channel.builder()
.channelId(1L)
.channelName("XXX")
.channelType(1)
.createdTime(new Date())
.build();
}
}
# 3.2. Lombok
实体添加 @Builder
注解即可
/**
* 渠道
*
* @author wliduo[i@dolyw.com]
* @date 2022/2/11 14:13
*/
@Data
@Builder
public class Channel {
/** 主键ID */
private Long channelId;
/** 渠道代码 */
private String channelCode;
/** 渠道名称 */
private String channelName;
/** 渠道类型 */
private Integer channelType;
/** 渠道状态:1-待审核 2-审核通过 3-合作中 4-暂停合作 5-废弃 */
private Integer status;
/** 渠道生效日期 */
private Date effectiveDate;
/** 渠道失效日期 */
private Date invalidDate;
/** 渠道联系人 */
private String channelContactName;
/** 渠道联系电话 */
private String channelContactPhone;
/** 渠道联系邮箱 */
private String channelContactEmail;
/** 创建人 */
private String createdBy;
/** 创建时间 */
private Date createdTime;
}
/**
* 建造者模式
*
* @author wliduo[i@dolyw.com]
* @date 2022/2/11 14:27
*/
public class Main {
public static void main(String[] args) {
App app = App.Builder()
.withAppId(1L)
.withAppCode("App")
.withAppName("XX系统")
.withCreatedBy("XXX")
.build();
Channel channel = Channel.builder()
.channelId(1L)
.channelName("XXX")
.channelType(1)
.createdTime(new Date())
.build();
}
}
# 3.3. AlertDialog
通常是一个对象,具有多个成员变量可能需要初始化,常规方法,需要提供大量构造函数。例如:
/**
* 非 Android 中的 AlertDialog,便于说明问题,举个例子
*/
public class AlertDialog {
private int width;
private int height;
private String title;
private String confirmText;
private String denyText;
private AlertDialog() {
}
/**
* 空白的警告框
*
* @param width
* @param height
*/
public AlertDialog(int width, int height) {
AlertDialog(width, height, null);
}
/**
* 带标题的警告框
*
* @param width
* @param height
* @param title
*/
public AlertDialog(int width, int height, String title) {
AlertDialog(width, height, title, "确定");
}
/**
* 带标题的警告框,有确定按钮
*
* @param width
* @param height
* @param title
* @param confirm
*/
public AlertDialog(int width, int height, String title, String confirm) {
AlertDialog(width, height, title, confirm, null);
}
/**
* 带标题的警告框,有确定按钮,取消按钮
*
* @param width
* @param height
* @param title
* @param confirm
* @param denyText
*/
public AlertDialog(int width, int height, String title, String confirm, String denyText) {
// set every thing.
}
}
有多种样式的警告框,为了调用方便,必须提供多个构造函数。否则用户在调用时,只能使用完整构造函数,容易犯错且无法进行阅读。极不灵活。如果采用另外一种方式,则可以解决,但会花费很多经历处理并发的情况:
/**
* 非 Android 中的 AlertDialog,便于说明问题,举个例子
*/
public class AlertDialog {
private int width;
private int height;
private String title;
private String confirmText;
private String denyText;
/**
* 空白的构造函数
*/
public AlertDialog() {
}
public void setWidth(int width) {
this.width = width;
}
// 其他set方法
}
调用时,通过调用各个参数的set方法进行设置。问题来了:
并发 无法进行参数校验。 例如,只创建了对象,设置了标题,却没有尺寸,相当于创建了一个没有尺寸的警告框
在 Android 中,大量的控件都使用了构造器 Builder
/**
* 非 Android 中的 AlertDialog,便于说明问题,举个例子
*/
public class AlertDialog {
private int width;
private int height;
private String title;
private String confirmText;
private String denyText;
/**
* private
*/
private AlertDialog() {
}
/**
* Builder中使用
*
* @param b
*/
protected AlertDialog(Builder b) {
width = b.width;
height = b.height;
// .....
if (width == 0 || height == 0) {
throws new Exception("size must be set");
}
}
/**
* 构造器
*/
public static class Builder {
private int width;
private int height;
private String title;
private String confirmText;
private String denyText;
/**
* 注意:返回的Builder
*
* @param title
* @return
*/
public Builder setTitle(String title) {
this.title = title;
return this;
}
// 其他set...
public AlertDialog build() {
return AlertDialog(this);
}
}
}
于是,可以根据相应需求,进行相应设置,并在AlertDialog真正构造时,进行参数校验。就像这样:
new AlertDialog.Builder().setTitle("提示").build();
上述例子,会成功抛出异常