What
为了灵活构造复杂对象,该对象会有多个成员变量,在外部调用的时候,不需要或者不方便一次性创建出所有的成员变量,在这种情况下,使用多个构造方法去构建对象,很难维护,这时候Builder设计模式解决这个问题,进行buid()方法中创建对象,并且将builder传入,该builder中,维护了传入对象的成员变量。
- 与工厂模式的区别:
工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。
Why
如果一个类中有很多属性,为了避免构造函数的参数列表过长,影响代码的可读性和易用性,我们可以通过构造函数配合 set() 方法来解决。但是,如果存在下面情况中的任意一种,我们就要考虑使用建造者模式了。
- 我们把类的必填属性放到构造函数中,强制创建对象的时候就设置。如果必填的属性有很多,把这些必填属性都放到构造函数中设置,那构造函数就又会出现参数列表很长的问题。如果我们把必填属性通过 set() 方法设置,那校验这些必填属性是否已经填写的逻辑就无处安放了。
- 如果类的属性之间有一定的依赖关系或者约束条件,我们继续使用构造函数配合 set() 方法的设计思路,那这些依赖关系或约束条件的校验逻辑就无处安放了。
- 如果我们希望创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值,要实现这个功能,我们就不能在类中暴露 set() 方法。构造函数配合 set() 方法来设置属性值的方式就不适用了。
How
这里,以电商平台里常见的搜索商品的搜索条件,来向大家介绍建造者模式的经典实现。需求是这样的:
- 用户名
username
是必传项,如果username
为空,直接抛出异常; -
useCategory
和category
为可选项,如果useCategory
为true
,代表使用类别信息去筛选,加快搜索效率。此时,category不能为空,否则抛异常; -
fuzzy
代表是否开启模糊查询,如果为true
时,搜素关键字keyword
和category
(如果不为空的情况下)前后添加%
,方便构造SQL语句;
建造者模式实现起来很简单,直接上代码:
public class QueryCondition {
private String username;
private Boolean useCategory;
private String category;
private Boolean fuzzy;
private String keyword;
private Integer pageSize;
private Integer pageNo;
private QueryCondition(QueryConditionBuilder builder) {
this.username = builder.username;
this.useCategory = builder.useCategory;
this.category = builder.category;
this.fuzzy = builder.fuzzy;
this.keyword = builder.keyword;
this.pageSize = builder.pageSize;
this.pageNo = builder.pageNo;
}
@Override
public String toString() {
return String.format("%s is querying products, the query condition is [useCategory = %s," +
"category = %s, " +
"fuzzy = %s, " +
"keyword = '%s', " +
"pageSize = %d, " +
"pageNo = %d]", username, useCategory, category, fuzzy, keyword, pageSize, pageNo);
}
public static class QueryConditionBuilder {
private String username;
private Boolean useCategory;
private String category;
private Boolean fuzzy;
private String keyword;
private Integer pageSize;
private Integer pageNo;
public QueryConditionBuilder username(String username) {
this.username = username;
return this;
}
public QueryConditionBuilder useCategory(Boolean useCategory) {
this.useCategory = useCategory;
return this;
}
public QueryConditionBuilder category(String category) {
this.category = category;
return this;
}
public QueryConditionBuilder fuzzy(Boolean fuzzy) {
this.fuzzy = fuzzy;
return this;
}
public QueryConditionBuilder keyword(String keyword) {
this.keyword = keyword;
return this;
}
public QueryConditionBuilder pageSize(Integer pageSize) {
this.pageSize = pageSize;
return this;
}
public QueryConditionBuilder pageNo(Integer pageNo) {
this.pageNo = pageNo;
return this;
}
public QueryCondition build() {
if (this.username == null || this.username.isEmpty()) {
throw new IllegalArgumentException("username can not be empty");
}
if (this.useCategory) {
if (this.category == null || this.category.isEmpty()) {
throw new IllegalArgumentException("category can not be empty when useCategory is true");
}
}
if (this.fuzzy) {
if (this.keyword != null && !this.keyword.isEmpty()) {
this.keyword = "% " + this.keyword + " %";
}
if (this.category != null && !this.category.isEmpty()) {
this.category = "%" + this.category + "%";
}
}
return new QueryCondition(this);
}
}
}
代码地址
写在最后
如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~