一、前言
什么是建造者模式?或许我们对这个模式一点都不了解的。但链式调用你总写过吧,那就是建造者模式。
我举个栗子:
这是一个okhttp3使用的栗子:
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("Cookie", cookieString)
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko")
.build();
让我们深入Request类的内部,仅保留了url字段,简化后的Request如下:
public final class Request {
private final HttpUrl url;
private Request(Builder builder) {
this.url = builder.url;
}
public HttpUrl url() {
return this.url;
}
public static class Builder {
private HttpUrl url;
public Builder url(HttpUrl url) {
if (url == null) {
throw new NullPointerException("url == null");
} else {
this.url = url;
return this;
}
}
public Request build() {
if (this.url == null) {
throw new IllegalStateException("url == null");
} else {
return new Request(this);
}
}
}
}
我们可以发现:
- Request类中有一个静态内部类Builder,Builder类中的属性和Request类一一对应。
- Request类的构造方法接收的是一个Builder对象。
- Builder类中每一个设置属性的方法返回值都是Builder对象本身。
- Builder类中的build()方法调用Request类的构造方法,将Builder对象本身传入。
看的出来,建造者模式一步一步地将简单的对象构造成复杂的对象,我们熟悉的链式调用,就是建造者模式。
建造者模式本来应该有多个角色:
- Product 产品类,我们最终需要的产品,对应这里的Request
- AbstractBuilder 抽象的建造者,规范产品的组件
- ConcreteBuilder 具体的建造者,实现抽象的建造者中的方法,最后返回一个产品。对应这里的Builder
- Director 导演,封装了建造者,用于通知建造者开始建造
但随着建造者模式的简化,其中的AbstractBuilder、Director的概念已经淡化,或者说用得不多。最终演变成只有一个产品类,以及产品类中的静态建造者类。
二、实例演示
这里我们建造一个电脑,电脑由显示器、机箱、键盘和鼠标构成。
package com.yang.testBuilder;
public class Computer {
/**
* 显示器
*/
private String monitor;
/**
* 机箱
*/
private String box;
/**
* 键盘
*/
private String keyboard;
/**
* 鼠标
*/
private String mouse;
public Computer(Builder builder) {
this.monitor = builder.monitor;
this.box = builder.box;
this.keyboard = builder.keyboard;
this.mouse = builder.mouse;
}
public String getMonitor() {
return monitor;
}
public String getBox() {
return box;
}
public String getKeyboard() {
return keyboard;
}
public String getMouse() {
return mouse;
}
@Override
public String toString() {
return "Computer{" +
"monitor='" + monitor + '\'' +
", box='" + box + '\'' +
", keyboard='" + keyboard + '\'' +
", mouse='" + mouse + '\'' +
'}';
}
static class Builder {
/**
* 显示器
*/
private String monitor;
/**
* 机箱
*/
private String box;
/**
* 键盘
*/
private String keyboard;
/**
* 鼠标
*/
private String mouse;
public Builder monitor(String monitor) {
this.monitor = monitor;
return this;
}
public Builder box(String box) {
this.box = box;
return this;
}
public Builder keyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Builder mouse(String mouse) {
this.mouse = mouse;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
测试:
package com.yang.testBuilder;
public class TestBuilderMain {
public static void main(String[] args) {
Computer computer = new Computer.Builder()
.monitor("显示器")
.box("机箱")
.keyboard("键盘")
.mouse("鼠标").build();
System.out.println(computer);
}
}
输出:
链式调用的好处在哪里?
链式调用比(无参构造方法+一个一个set)更加节省代码,更加简便。
链式调用比普通的全参构造方法更加简明,全参构造方法需要将参数设置进对应的位置上,在idea中,我们需要按照ctrl+p才能显示当前参数的名称,这一点没有链式调用方便。