设计模式-建造者模式
前言
在日常开发中,我们经常见到建造者模式的身影,我们看下建造者模式的定义
The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示
场景
当一个类需要很多种不同的构造函数,且在不同场景有不同的参数可选需求,这时候可以使用建造者模式来简化使用方式。
问题所在:
如下是一个User类:
public class User {
private String name;
private String sex;
private Integer age;
private String language;
private String area;
}
通常我们会使用两种方式来使用这个类
一个是构造方法:
public User(String name) {
this.name = name;
}
public User(String name, String sex) {
this.name = name;
this.sex = sex;
}
public User(String name, String sex, Integer age) {
this.name = name;
this.sex = sex;
this.age = age;
}
若是当前类有非常多的属性,那么我们在复杂业务中可能创建大量的构造方法,这样在使用的时候,我们并不清楚哪种构造方法在哪种场景更合适使用;时间长了会阅读困难,且容易造成误解;
一个是set方法
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setAge(Integer age) {
this.age = age;
}
public void setLanguage(String language) {
this.language = language;
}
public void setArea(String area) {
this.area = area;
}
这种方式在构建过程中对象的状态容易发生变化,造成错误。因为类的属性是分步设置的;
建造者模式实现
public class User {
private String name;
private String sex;
private Integer age;
private String language;
private String area;
private User(Builder builder) {
this.name = builder.name;
this.sex = builder.sex;
this.age = builder.age;
this.language = builder.language;
this.area = builder.area;
}
public static class Builder {
private String name;
private String sex;
private Integer age;
private String language;
private String area;
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setSex(String sex) {
this.sex = sex;
return this;
}
public Builder setAge(Integer age) {
this.age = age;
return this;
}
public Builder setLanguage(String language) {
this.language = language;
return this;
}
public Builder setArea(String area) {
this.area = area;
return this;
}
public User build() {
return new User(this);
}
}
}
乍一看这样写法觉得很复杂,但是当你使用起来你会发现:
User user = new Builder()
.setName("小明")
.setAge(12)
.setSex("男")
.build();
和lombok的@Builder一样;
场景
有人说了,我这样操作一波有什么意义呢?我直接一个一个set也是完全Ok的。
那我们随意找一个在开源代码中的使用,在平常我们会经常使用线程池,我们便说一下线程池中的建造者模式的使用。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
我们熟知线程池有七个参数,且每个参数的意思也都很清楚;
return new ThreadPoolExecutor(4, 4, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
// TODO
return null;
}
}, new ThreadPoolExecutor.AbortPolicy());
假设我们new了一个这样的线程池,那么如果实现ThreadFactory接口就比较麻烦,因为我们需要new一个完整的线程对象;那么怎么简化这样的操作呢?
在google的guava包中便有这样一个类ThreadFactoryBuilder,让我们来看看他帮我们做了什么?
看一下这个类的组成,可以清楚看见这是一个建造者模式的完整实现,我们可以使用builder来快速的构建一个线程对象;
new ThreadFactoryBuilder().setNameFormat("test-thread-%d").build()
这样对比,是否简单明了很多?