有一个类有很多字段,所有字段都是必需的。 java代码如下:
public class MyCls {
private String aaa;
private String bbb;
private String ccc;
private String ddd;
private String eee;
private String fff;
private String ggg;
public MyCls(String aaa, String bbb, String ccc, String ddd, String eee, String fff, String ggg) {
this.aaa = aaa;
this.bbb = bbb;
this.ccc = ccc;
this.ddd = ddd;
this.eee = eee;
this.fff = fff;
this.ggg = ggg;
}
// here are getters for all fields, no setters
}
来电者代码为:
new MyCls("111","222","333","444","555","666","777");
我的朋友说要找到每个参数的含义并不容易,所以要为它创建一个构建器。这是代码:
package builder;
public class MyClsBuilder {
private String aaa;
private String bbb;
private String ccc;
private String ddd;
private String eee;
private String fff;
private String ggg;
public MyClsBuilder setAaa(String aaa) {
this.aaa = aaa;
return this;
}
// similar code for 'bbb','ccc',...,'ggg'
public MyClsBuilder setGgg(String ggg) {
this.ggg = ggg;
return this;
}
public MyCls createMyCls() {
return new MyCls(aaa, bbb, ccc, ddd, eee, fff, ggg);
}
}
来电者代码为:
MyCls cls = new MyClsBuilder()
.setAaa("111").setBbb("222").setCcc("333")
.setDdd("444").setEee("555").setFff("666").setGgg("777")
.createMyCls();
好的,我们可以看到我们为每个参数使用的setter,它更清晰。但我有一个问题:
我们真的需要这样的建筑师吗?我看不出多少好处。
事实上,如果我们为原始代码添加setter,我们可以在不创建新类的情况下编写类似的代码:
MyCls cls = new MyClsBuilder();
cls.setAaa("111");
cls.setBbb("222");
cls.setCcc("333");
cls.setDdd("444");
cls.setEee("555");
cls.setFff("666");
cls.setGgg("777");
唯一的区别是不是链条。
要使用MyClsBuilder
,我不得不担心如果我忘记错误地拨打任何setX
。
MyCls cls = new MyClsBuilder()
.setAaa("111").setBbb("222").setCcc("333")
.setDdd("444").setEee("555").setFff("666")
.createMyCls();
// forget to invoke `.setGgg("777")`
但是使用“构造函数”,没有这样的问题,因为编译器会为我检查它。
所以我认为建筑师不是一个好的解决方案,是不是更好?
答案 0 :(得分:4)
事实上,如果我们为原始代码添加setter,我们可以在不创建新类的情况下编写类似的代码
是的,但仅当MyCls
可变时。有时你想要一个不会有setter的不可变对象。
要使用
MyClsBuilder
,我必须担心如果我忘记错误地拨打任何setX
。
如果需要所有参数,则构建器不太有用。如果有很多参数并且大多数参数都可以可选,那么构建器非常有用,因为构建器可以使用默认值初始化它们。
使用普通构造函数,实现可选参数需要几个重载的构造函数(令人困惑),或者让用户指定他们不关心的参数的默认值(分散注意力)。构建器使这更容易,因为您只需要指定您关心的几个设置。
答案 1 :(得分:1)
这是为了解决Freewind的观察,即简单的计数器方法不适合意外地设置两次参数。为了清楚起见,我使用了一个位集,但是int计数器可以以相同的方式使用,但是位集可能更优雅而且不那么神秘。
private BitSet parameterBits = new BitSet(7); // there are 7 parameters
public MyClsBuilder setAaa(String aaa)
{
parameterBits.set(0); // set bit position 0 to true
this.aaa = aaa;
return this;
}
public MyClsBuilder setBbb(String bbb)
{
parameterBits.set(1);
this.bbb = bbb;
return this;
}
public MyClsBuilder setGgg(String ggg)
{
parameterBits.set(6);
this.ggg = ggg;
return this;
}
public MyCls createMyCls()
{
if ( parameterBits.cardinality() < parameterBits.length() )
{
throw new Exception("Not all parameters supplied.");
}
return new MyCls(aaa, bbb, ccc, ddd, eee, fff, ggg);
}
答案 2 :(得分:0)
构造函数版本的一个缺点/问题是,在您的特定情况下,您可能会意外地交换两个参数。对于建筑商而言,这种情况不太可能发生。
如果需要所有参数,仍然可以使用构建器,只需要更多代码来检查是否已提供所有参数。
一种简单的方法(但不一定是最好的方法)就是让计数器在设置参数时递增:
private int counter = 0;
public MyClsBuilder setAaa(String aaa)
{
counter++;
this.aaa = aaa;
return this;
}
public MyCls createMyCls()
{
if ( counter < 7 ) throw new Exception("Not all parameters supplied.");
return new MyCls(aaa, bbb, ccc, ddd, eee, fff, ggg);
}
因此,我认为构建器不易出错且更易读。清晰的代码是很好的代码: - )