当某个类的参数过多,构造细节过于复杂时,使用builder
模式。build()
方法可以做一些参数有效性的校验工作
不太好的地方:Builder
类会把主类中的参数做一层冗余。这个在内部看来不太好看。也让人觉得不舒服。在外面用的时候确实比较爽。说明这样的爽是有代价的。
建造者模式与工场模式的区别:
① 建造者模式,重点关注在构造某个类的细节上
② 工场模式,重点关注在产品的种类上
一家必胜客,可以生产牛肉披萨,鸡肉披萨,培根披萨。这种分类使用工场模式来搞。具体牛肉披萨的生产过程,使用建造着模式来搞。他们都是创建,但不冲突。关注点有粗细之分。
这边以com.alibaba.dubbo.common.URL
构造作为案例说明下。
// URL 出自 Dubbo源码,版本2.6.4
// 它原本使用的是多个构造方法的形式。
// 我觉得这边参数较多。在外面使用时,每个参数的提现不是很明显,修改成Builder模式更优一些。
public final class URL implements Serializable {
private static final long serialVersionUID = -1985165475234910535L;
private final String protocol;
private final String username;
private final String password;
// by default, host to registry
private final String host;
// by default, port to registry
private final int port;
private final String path;
private final Map<String, String> parameters;
// ==== cache ====
private volatile transient Map<String, Number> numbers;
private volatile transient Map<String, URL> urls;
private volatile transient String ip;
private volatile transient String full;
private volatile transient String identity;
private volatile transient String parameter;
private volatile transient String string;
protected URL() {
this.protocol = null;
this.username = null;
this.password = null;
this.host = null;
this.port = 0;
this.path = null;
this.parameters = null;
}
public URL(String protocol, String host, int port) {
this(protocol, null, null, host, port, null, (Map<String, String>) null);
}
public URL(String protocol, String host, int port, String[] pairs) { // varargs ... confilict with the following path argument, use array instead.
this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs));
}
public URL(String protocol, String host, int port, Map<String, String> parameters) {
this(protocol, null, null, host, port, null, parameters);
}
public URL(String protocol, String host, int port, String path) {
this(protocol, null, null, host, port, path, (Map<String, String>) null);
}
public URL(String protocol, String host, int port, String path, String... pairs) {
this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs));
}
public URL(String protocol, String host, int port, String path, Map<String, String> parameters) {
this(protocol, null, null, host, port, path, parameters);
}
public URL(String protocol, String username, String password, String host, int port, String path) {
this(protocol, username, password, host, port, path, (Map<String, String>) null);
}
public URL(String protocol, String username, String password, String host, int port, String path, String... pairs) {
this(protocol, username, password, host, port, path, CollectionUtils.toStringMap(pairs));
}
public URL(String protocol, String username, String password, String host, int port, String path, Map<String, String> parameters) {
if ((username == null || username.length() == 0)
&& password != null && password.length() > 0) {
throw new IllegalArgumentException("Invalid url, password without username!");
}
this.protocol = protocol;
this.username = username;
this.password = password;
this.host = host;
this.port = (port < 0 ? 0 : port);
// trim the beginning "/"
while (path != null && path.startsWith("/")) {
path = path.substring(1);
}
this.path = path;
if (parameters == null) {
parameters = new HashMap<String, String>();
} else {
parameters = new HashMap<String, String>(parameters);
}
this.parameters = Collections.unmodifiableMap(parameters);
}
}
// 使用Builder模式重构之后
public final class URL implements Serializable {
private final String protocol;
private final String username;
private final String password;
// by default, host to registry
private final String host;
// by default, port to registry
private final int port;
private final String path;
private final Map<String, String> parameters;
public static class Builder {
private final String protocol;
private String username;
private String password;
// by default, host to registry
private String host;
// by default, port to registry
private int port;
private String path;
private Map<String, String> parameters;
public Builder(String protocol) {
this.protocol = protocol;
}
public Builder setUsernameAndPassword(String username, String password) {
if ((username == null || username.length() == 0)
&& password != null && password.length() > 0) {
throw new IllegalArgumentException("Invalid url, password without username!");
}
this.username = username;
this.password = password;
return this;
}
public Builder setHost(String host) {
this.host = host;
return this;
}
public Builder setPort(int port) {
this.port = Math.max(port, 0);
;
return this;
}
public Builder setPath(String path) {
while (path != null && path.startsWith("/")) {
path = path.substring(1);
}
this.path = path;
return this;
}
public Builder setParameters(Map<String, String> parameters) {
this.parameters = Collections.unmodifiableMap(parameters == null ? new HashMap<>() : new HashMap<>(parameters));
return this;
}
public URL build() {
return new URL(this.protocol, this.username, this.password, this.host, this.port, this.path, this.parameters);
}
}
public URL(String protocol, String username, String password, String host, int port, String path, Map<String, String> parameters) {
this.protocol = protocol;
this.username = username;
this.password = password;
this.host = host;
this.port = port;
this.path = path;
this.parameters = parameters;
}
}
// 在URL对象构建时,对于参数的设置会更加明显,方便阅读
URL url = new URL.Builder("dubbo")
.setUsernameAndPassword("abc", "abc")
.setHost("localhost")
.setPort(20881)
.setPath("/dubbo")
.setParameters(null)
.build();
查看全部 浅谈模式 - 汇总篇