它是一个好建设者吗?

时间:2013-12-29 15:41:52

标签: java builder

有一个类有很多字段,所有字段都是必需的。 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")`

但是使用“构造函数”,没有这样的问题,因为编译器会为我检查它。

所以我认为建筑师不是一个好的解决方案,是不是更好?

3 个答案:

答案 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);
}

因此,我认为构建器不易出错且更易读。清晰的代码是很好的代码: - )