一、介绍
生成器模式是什么呢?
《研磨设计模式中》给了这样的定义:将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表示。
生成器模式的本质——分离整体构建算法和部件构造。
其实,生成器模式的目的,便是为了构建复杂的产品,将构建算法和构造实现分离出来,以便系统可以更好的优化、扩展。
理解生成器模式主要是理解生成器2个部件,一个是生成器(Builder),一个是指导者(Director)。生成器指的是整个复杂对象的构建过程、构建算法。而指导者指的是对生成器所生成的部件对象的构造和装配。
二、结构
1、Product——需要最终构建的复杂对象。
2、Director——指导者。用来使用生成器接口,并定义一个整体的构建算法,定义产品构建中不变的那些部分,而其他部分交给Builder来实现。
3、Builder——生成器接口。定义创建一个Product所需的各个部件的操作。
4、ConcreteBuilder——具体的生成器实现。实现Product各个组件的创建,并且负责组装各个部件。同时还可以获得整个产品。
研磨设计模式里面有个很形象的图,这里拿来用一下:
三、我的实现
下面我们用一个例子来解释生成器模式。
1、问题:假设我们要生产水果罐头,大体上有这么3个环节——材料准备、加工、包装。这3个环节都很复杂,需要复杂的设计和管理技术。我们希望能尽可能的将这3个环节的构建和这3个环节的组合从复杂的类层次中独立出来,以便系统升级、优化!生成器模式可以很好的解决这个问题。
首先,3个环节可以抽象成这样3个简单的类,如下:
//预处理
public class Pretreatment {
//这里只列出一个成员变量,实际还会有很多
private Fruit fruit;
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
public Fruit getFruit() {
return fruit;
}
}
//制作过程
public class Proceed {
// 这里只列出一个成员变量,实际还会有很多
private Cook cook;
public Cook getCook() {
return cook;
}
public void setCook(Cook cook) {
this.cook = cook;
}
}
//包装
public class Pack {
// 这里只列出一个成员变量,实际还会有很多
private Can can;
public Can getCan() {
return can;
}
public void setCan(Can can) {
this.can = can;
}
}
2、为了构建整个过程,需要一个生成器,为了方便扩展,所以实现成接口,如下:
package builder.toBuild;
//生成器接口
public interface Builder {
//把前面3个类作为形参传进来
public void pretreatment(Pretreatment pre);
public void proceed(Proceed pro);
public void pack(Pack pack);
}
3、生成器接口的实现类,就是我们抽象出来的整个过程、构建逻辑的核心了。如下:
//生成器的实现类,封装了复杂对象的具体生成逻辑。
public class BuilderImpl1 implements Builder {
public void pretreatment(Pretreatment pre) {
System.out.println("现在是预处理部!");
System.out.println("精心选用上等"+pre.getFruit().getName()+",剥皮,洗干净!");
}
public void proceed(Proceed proceed) {
System.out.println("现在是加工部!");
System.out.println("加工过程中,我们采用\""+proceed.getCook().getCookName()+"\"的先进工艺");
}
public void pack(Pack pack) {
System.out.println("现在是在包装部");
System.out.println("我们采用了"+pack.getCan().getName()+"进行了精心包装!");
}
}
4、但是我们还需要一个指导者,为生成器注入原材料,并将生成器生成的对象进行拼接组装,为了方便扩展,当然也是先处理为一个接口,如下:
//指导者接口
public interface Director {
//注入生成器对象
public void setBuilder(Builder builder);
//指导者进行将生成器生成的部分进行拼接
public void proceed(Pretreatment pre,Proceed proceed,Pack pack);
}
5、指导者实现类如下:
public class DirectorImpl1 implements Director{
private Builder builder;
//注入生成器对象
public void setBuilder(Builder builder) {
// TODO Auto-generated method stub
this.builder = builder;
}
//将生成器部件按简单的顺序进行组装
public void proceed(Pretreatment pre,Proceed proceed,Pack pack){
System.out.println("开始!");
builder.pretreatment(pre);
builder.proceed(proceed);
builder.pack(pack);
System.out.println("结束!");
}
}
6、客户端实现如下:
public class Client {
public static void main(String[] args) {
// 构建生成器
Builder builder = new BuilderImpl1();
// 通过生成器构建组织者
DirectorImpl1 director = new DirectorImpl1();
director.setBuilder(builder);
// 简单组件
Pretreatment pretreatment = new Pretreatment();
pretreatment.setFruit(new Apple("红富士"));
// 简单组件
Proceed proceed = new Proceed();
proceed.setCook(new Boil());
// 简单组件
Pack pack = new Pack();
Can can = new Can();
can.setName("铁制罐头");
pack.setCan(can);
// 组织者将简单组件注入到生成器中进行真正的组装,并将返回结果拼装。
director.proceed(pretreatment, proceed, pack);
}
}
最后结果:
为了避免繁琐,演示中许多简单的JavaBean被省略了,为了看的方便,类的包名、import等都被我删去了。
Builder 模式实例应用
1. 在 okHttp 中 Request属性
Request 有6个属性,按照套路 构造方法持有一个 Builder ,在构造中将 builder 制造的组件赋值给 Request 完成构建,提供 newBuilder 用于重新获得 Builder 返厂重建
final HttpUrl url;
final String method;
final Headers headers;
final RequestBody body;
final Object tag;
private volatile CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
public Builder newBuilder() {
return new Builder(this);
}
Builder 有两个构造,第一个空构造中初始化两个默认值。第二个构造持有 Request 用于重新构建 Builder 返厂重建。
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tag = request.tag;
this.headers = request.headers.newBuilder();
}
剩下的就是一些属性初始化的方法,返回值为 Builder 方便链式调用。这里就列出一个方法,详细的请查看源码,最后调用 build() 方法 初始化 Request 传入 Builder 完成构建。
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
...此处省略部分方法
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
调用
Request request = new Request.Builder()
.url("http://www.youtube.com")
.addHeader("header","header")
.put("RequestBody")
.build();
全部源码


public final class Request { final HttpUrl url; final String method; final Headers headers; final RequestBody body; final Object tag; Request(Builder builder) { this.url = builder.url; this.method = builder.method; this.headers = builder.headers.build(); this.body = builder.body; this.tag = builder.tag != null ? builder.tag : this; } public HttpUrl url() { return url; } public String method() { return method; } public Headers headers() { return headers; } public String header(String name) { return headers.get(name); } public List<String> headers(String name) { return headers.values(name); } public RequestBody body() { return body; } public Object tag() { return tag; } public Builder newBuilder() { return new Builder(this); } public CacheControl cacheControl() { CacheControl result = cacheControl; return result != null ? result : (cacheControl = CacheControl.parse(headers)); } public boolean isHttps() { return url.isHttps(); } public static class Builder { HttpUrl url; String method; Headers.Builder headers; RequestBody body; Object tag; public Builder() { this.method = "GET"; this.headers = new Headers.Builder(); } Builder(Request request) { this.url = request.url; this.method = request.method; this.body = request.body; this.tag = request.tag; this.headers = request.headers.newBuilder(); } public Builder url(HttpUrl url) { if (url == null) throw new NullPointerException("url == null"); this.url = url; return this; } public Builder url(String url) { if (url == null) throw new NullPointerException("url == null"); // Silently replace web socket URLs with HTTP URLs. if (url.regionMatches(true, 0, "ws:", 0, 3)) { url = "http:" + url.substring(3); } else if (url.regionMatches(true, 0, "wss:", 0, 4)) { url = "https:" + url.substring(4); } HttpUrl parsed = HttpUrl.parse(url); if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url); return url(parsed); } public Builder url(URL url) { if (url == null) throw new NullPointerException("url == null"); HttpUrl parsed = HttpUrl.get(url); if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url); return url(parsed); } public Builder header(String name, String value) { headers.set(name, value); return this; } public Builder addHeader(String name, String value) { headers.add(name, value); return this; } public Builder removeHeader(String name) { headers.removeAll(name); return this; } public Builder headers(Headers headers) { this.headers = headers.newBuilder(); return this; } public Builder cacheControl(CacheControl cacheControl) { String value = cacheControl.toString(); if (value.isEmpty()) return removeHeader("Cache-Control"); return header("Cache-Control", value); } public Builder get() { return method("GET", null); } public Builder head() { return method("HEAD", null); } public Builder post(RequestBody body) { return method("POST", body); } public Builder delete(RequestBody body) { return method("DELETE", body); } public Builder delete() { return delete(Util.EMPTY_REQUEST); } public Builder put(RequestBody body) { return method("PUT", body); } public Builder patch(RequestBody body) { return method("PATCH", body); } public Builder method(String method, RequestBody body) { if (method == null) throw new NullPointerException("method == null"); if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0"); if (body != null && !HttpMethod.permitsRequestBody(method)) { throw new IllegalArgumentException("method " + method + " must not have a request body."); } if (body == null && HttpMethod.requiresRequestBody(method)) { throw new IllegalArgumentException("method " + method + " must have a request body."); } this.method = method; this.body = body; return this; } public Builder tag(Object tag) { this.tag = tag; return this; } /* * Request 对象创建器,想得到一个Request 对象必须使用build 方法, * 在方法中增加对Builder参数的验证,并以异常的形式告诉给开发人员。 */ public Request build() { /** * 比如下面判断如果 url 是null的话就会抛出异常 */ if (url == null) throw new IllegalStateException("url == null"); return new Request(this); } } }