Builder 链——另类一点的Builder模式
在 对象创建的Builder模式中 一文中的 Builder 模式应该说还是蛮有用的,然而很多时候它还是有一点点不方便,例如 对象的属性比较多的时候,常常会比较担心 “有没有漏掉那个属性没有初始化呢?”,今天灵感突现,给这个问题找到了一个还算是不错的解决方案,我称之为 Builder 链,虽然代码稍微有点复杂,不过如果结合一个 “生成 Builder ”的代码生成工具的话,应该就可以圆满了。
场景:需要构造一些这样的 URL [cc:][//server[:port]]/[volume]/partition[:version][//filename] , 这里很多 [] 中的那些部分表示是可以省略的,因此使用了这样一个 Builder
private String cc;
private String server;
private String port;
private String volume;
private String partition;
private String version;
private String filename;
... ...
采用通常的 Builder 模式写法,那么每次在 urlBuilder 后面按下句点时,IDE 将弹出相应的一系列 setXXXX 方法,问题就在于我们需要从这一系列方法中挑出一个,并且要保证最后所有的 setXXXX 方法都被调用过了一次,属性太多的时候,这就成了一个大问题。
因此,现在我们这样来写这个 Builder
private String cc;
private String server;
private String port;
private String volume;
private String partition;
private String version;
private String filename;
private ServerSetter serverSetter = new ServerSetter();
private PortSetter portSetter = new PortSetter();
private VolumeSetter volumeSetter = new VolumeSetter();
private PartitionSetter partitionSetter = new PartitionSetter();
private VersionSetter versionSetter = new VersionSetter();
private FilenameSetter filenameSetter = new FilenameSetter();
public static CCUrlBuilder start() {
return new CCUrlBuilder();
}
public String get() {
// [cc:][//server[:port]]/[volume]/partition[:version][//filename]
StringBuilder sb = new StringBuilder();
if (cc!=null) sb.append(cc);
if (server!=null) sb.append("//").append(server);
if (port!=null) sb.append(":").append(port);
sb.append("/").append(volume == null? "" : volume);
sb.append("/").append(partition);
if (version!=null) sb.append(":").append(version);
if (filename!=null) sb.append("//").append(filename);
return sb.toString();
}
public ServerSetter setCc(String cc) {
this.cc = cc;
return serverSetter;
}
public ServerSetter setCc() {
return serverSetter;
}
private class ServerSetter {
public PortSetter setServer(String value) {
server = value;
return portSetter;
}
public PortSetter setServer() {
return portSetter;
}
}
private class PortSetter {
public VolumeSetter setPort(int value) {
port = Integer.toString(value);
return volumeSetter;
}
public VolumeSetter setPort() {
return volumeSetter;
}
}
private class VolumeSetter {
public PartitionSetter setVolume (String value) {
volume = value;
return partitionSetter;
}
public PartitionSetter setVolume () {
return partitionSetter;
}
}
public class PartitionSetter {
public VersionSetter setPartition (String value) {
partition = value;
return versionSetter;
}
public VersionSetter setPartition () {
return versionSetter;
}
}
public class VersionSetter {
public FilenameSetter setVersion(int value) {
version = Integer.toString(value);
return filenameSetter;
}
public FilenameSetter setVersion() {
return filenameSetter;
}
}
public class FilenameSetter {
public CCUrlBuilder setFilename(String value) {
filename = value;
return CCUrlBuilder.this;
}
public CCUrlBuilder setFilename() {
return CCUrlBuilder.this;
}
}
}
贴这么大一段代码实在是情非所愿,不过这段代码本身应该已经说明了这种模式所采用的方法,简单的说,就是用很多的内部类来代替原来 Builder 中的 setXXXX 方法,每个类只提供一个 setXXXX 方法,并返回下一个类,最后再回到 Builder 本身,通过 get() 方法返回构造好的对象。
这样做最大的好处就是由于所有的 XXXXSetter 类,构成了一个链,所以在使用 Builder 的时候,将迫使我们必须将所有的属性都过一篇才能最后回到 Builder 本身,自然就不会遗漏任何一个属性了,其次,每次按下 句点 符号时,IDE 只会弹出唯一的一个 setXXXX 方法,不需要费心 去挑选了,最后,这些 Setter 类还提供了无参的方法,可以方便我们快速的跳过一个属性的设置,但又不会再代码上留下空白,一眼看过,就知道跳过了那些属性,以后要补上这些跳过的属性也非常的方便。