Builder 模式,也叫生成器模式。创建者模式主要包含以下四个角色:
- 产品(Product):表示将要被构建的复杂对象。
- 抽象创建者(Abstract Builder):定义构建产品的接口,通常包含创建和获取产品的方法。
- 具体创建者(Concrete Builder):实现抽象创建者定义的接口,为产品的各个部分提供具体实现。
- 指挥者(Director):负责调用具体创建者来构建产品的各个部分,控制构建过程。
假设我们需要创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。可以使用创建者设计模式来构建HTML文档。
1、产品(Product)类 - HTML文档(HtmlDocument):
public class HtmlDocument {
private String header = "";
private String body = "";
private String footer = "";
public void addHeader(String header) {
this.header = header;
}
public void addBody(String body) {
this.body = body;
}
public void addFooter(String footer) {
this.footer = footer;
}
@Override
public String toString() {
return "<html><head>" + header + "</head><body>" + body + "</body><footer>" + footer + "</footer></html>";
}
}
2、抽象创建者(Abstract Builder)类 - HtmlDocumentBuilder:
public abstract class HtmlDocumentBuilder {
protected HtmlDocument document;
public HtmlDocument getDocument() {
return document;
}
public void createNewHtmlDocument() {
document = new HtmlDocument();
}
public abstract void buildHeader();
public abstract void buildBody();
public abstract void buildFooter();
}
3、具体创建者(Concrete Builder)类 - ArticleHtmlDocumentBuilder:
public class ArticleHtmlDocumentBuilder extends HtmlDocumentBuilder {
@Override
public void buildHeader() {
document.addHeader("Article Header");
}
@Override
public void buildBody() {
document.addBody("Article Body");
}
@Override
public void buildFooter() {
document.addFooter("Article Footer");
}
}
4、指挥者(Director)类 - HtmlDirector:
public class HtmlDirector {
private HtmlDocumentBuilder builder;
public HtmlDirector(HtmlDocumentBuilder builder) {
this.builder = builder;
}
public void constructDocument() {
builder.createNewHtmlDocument();
builder.buildHeader();
builder.buildBody();
builder.buildFooter();
}
public HtmlDocument getDocument() {
return builder.getDocument();
}
}
使用创建者设计模式来构建一个HTML文档对象:
public class Main {
public static void main(String[] args) {
HtmlDocumentBuilder articleBuilder = new ArticleHtmlDocumentBuilder();
HtmlDirector director = new HtmlDirector(articleBuilder);
director.constructDocument();
HtmlDocument document = director.getDocument();
System.out.println("Constructed HTML Document: \n" + document);
}
}
在这个例子中创建了一个表示HTML文档的产品类(HtmlDocument),一个抽象的创建者类(HtmlDocumentBuilder),一个具体的创建者类(ArticleHtmlDocumentBuilder)和一个指挥者类(HtmlDirector)。当需要创建一个新的HTML文档对象时,可以使用指挥者类来控制构建过程,从而实现了将构建过程与表示过程的分离。
以上是一个创建者设计模式的标准写法,事实,在工作中往往不会写的这么复杂,为了创建一个对象,创建了很多辅助的类,总觉得不太合适,在这个案例中,可以使用内部类来简化代码,以下是修改后的代码(甚至我们还移除了抽象层):
public class HtmlDocument {
private String header = "";
private String body = "";
private String footer = "";
public void addHeader(String header) {
this.header = header;
}
public void addBody(String body) {
this.body = body;
}
public void addFooter(String footer) {
this.footer = footer;
}
@Override
public String toString() {
return "<html><head>" + header + "</head><body>" + body + "</body><footer>" + footer + "</footer></html>";
}
public static class Builder {
protected HtmlDocument document;
public Builder() {
document = new HtmlDocument();
}
public Builder addHeader(String header) {
document.addHeader(header);
return this;
}
public Builder addBody(String body) {
document.addBody(body);
return this;
}
public Builder addFooter(String footer) {
document.addFooter(footer);
return this;
}
public HtmlDocument build() {
return document;
}
}
}
现在使用以下代码来创建一个HTML文档对象:
public class Main {
public static void main(String[] args) {
HtmlDocument.ArticleBuilder builder = new HtmlDocument.ArticleBuilder();
HtmlDocument document = builder.addHeader("This is the header")
.addBody("This is the body")
.addFooter("This is the footer")
.build();
System.out.println("Constructed HTML Document: \n" + document);
}
}
在这个修改后的例子中,将创建者类(Builder)作为HTML文档类(HtmlDocument)的内部类。这样做可以让代码更加紧凑。此外,使用了一种流式接口(Fluent Interface),使得在客户端代码中创建HTML文档对象更加简洁。
当然有些人看了这个案例,已经会觉得
需求一、根据复杂的配置项进行定制化构建
首先,先看一个mybaits中经典的案例,这个案例中使用了装饰器和创建者设计模式:
public class CacheBuilder {
private final String id;
private Class<? extends Cache> implementation;
private final List<Class<? extends Cache>> decorators;
private Integer size;
private Long clearInterval;
private boolean readWrite;
private Properties properties;
private boolean blocking;
public CacheBuilder(String id) {
this.id = id;
this.decorators = new ArrayList<>();
}
public CacheBuilder size(Integer size) {
this.size = size;
return this;
}
public CacheBuilder clearInterval(Long clearInterval) {
this.clearInterval = clearInterval;
return this;
}
public CacheBuilder blocking(boolean blocking) {
this.blocking = blocking;
return this;
}
public CacheBuilder properties(Properties properties) {
this.properties = properties;
return this;
}
public Cache build() {
setDefaultImplementations();
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
// 根据配置的装饰器对原有缓存进行增强,如增加淘汰策略等
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
cache = new LoggingCache(cache);
}
return cache;
}
}
这个案例中的几个特点:
1、参数有必填项id,有很多可选填的内容,通常必选项id通过构造器传入,可选项通过方法传递。
2、真正的构建过程需要调用build方法,构建时需要根据已配置的成员变量的内容选择合适的装饰器,对目标cache进行增强。
需求二、实现不可变对象
创建者设计模式(Builder Design Pattern)可以实现不可变对象,即一旦创建完成,对象的状态就不能改变。这有助于保证对象的线程安全和数据完整性。下面是一个使用创建者设计模式实现的不可变对象的Java示例:
public final class ImmutablePerson {
private final String name;
private final int age;
private final String address;
private ImmutablePerson(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.address = builder.address;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
public static class Builder {
private String name;
private int age;
private String address;
public Builder() {
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setAddress(String address) {
this.address = address;
return this;
}
public ImmutablePerson build() {
return new ImmutablePerson(this);
}
}
}
在这个例子中,ImmutablePerson
类具有三个属性:name
、age
和 address
。这些属性都是 final
的,一旦设置就不能更改。ImmutablePerson
的构造函数是私有的,外部无法直接创建该类的实例。要创建一个 ImmutablePerson
实例,需要使用内部的 Builder
类。通过连续调用 Builder
类的方法,我们可以为 ImmutablePerson
设置属性。最后,通过调用 build()
方法,创建一个具有指定属性的不可变 ImmutablePerson
实例。
实际上,使用建造者模式创建对象,还能避免对象存在无效状态。如定义了一个长方形类,如果不使用建造者模式,采用先创建后 set 的方式,那就会导致在第一个 set 之后,对象处于无效状态。具体代码如下所示:
Rectangle r = new Rectange(); // r is invalid
r.setWidth(2); // r is invalid
r.setHeight(3); // r is valid
为了避免这种无效状态的存在,就需要使用构造函数一次性初始化好所有的成员变量。如果构造函数参数过多,就需要考虑使用建造者模式,先设置建造者的变量,然后再一次性地创建对象,让对象一直处于有效状态。
实际上,如果我们并不是很关心对象是否有短暂的无效状态,也不是太在意对象是否是可变的。比如,对象只是用来映射数据库读出来的数据,那我们直接暴露 set() 方法来设置类的成员变量值是完全没问题的。而且,使用建造者模式来构建对象,代码实际上是有点重复的。