Java中使用继承的建造者模式

3

我希望使用静态内部类实现构建者模式,例如对于类A(具有字段a1、a2、a3)、类B(具有字段b1、b2)和类C(具有字段c1),它们都从超类SuperClass继承字段s1、s2:

public class A extends SuperClass {
    private final String a1;
    ...

    private A(ABuilder builder) {
        super(builder);
        this.a1 = builder.a1;
        ...
    }

    public static class ABuilder extends SuperClassBuilder implements ABuilderInterface {
        private String a1;
        ...

        @Override
        public ABuilder withA1(String a1) {
            this.a1 = a1;
            return this;
        }
        ...

        @Override
        public SuperClass build() {
            return new A(this);
        }
    }
}

针对B和C的情况,建造者只是不同之处在于它们有自己的字段并实现了自己的接口(BBuilderInterface 和 CBuilderInterface),而这些接口仅定义了要实现哪些方法。
public interface ABuilderInterface extends SuperClassBuilderInterface {
    ABuilderInterface withA1(String a1);
    ...
}
...<interfaces for B and C>

public interface SuperClassBuilderInterface {
   SuperClassBuilderInterface withS1(String s1);
   ...
   SuperClass build();
}

// Usage of the builders:
public SuperClass foo() {
    return new A.ABuilder()
        .withA1(...) // returns ABuilderInterface
        ...
        .withS1(...) // returns SuperClassBuilderInterface
        ...
        .build();
}

public abstract class SuperClass {
private final String s1;
...

protected SuperClass(SuperClassBuilder builder) {
    this.s1 = builder.s1;
    ...
}

protected static abstract class SuperClassBuilder implements SuperClassBuilderInterface {
    private String s1;
    ...

    @Override
    public SuperClassBuilder withS1(String s1) {
        this.s1 = s1;
        return this;
    }
    ...

    @Override
    public abstract SuperClass build();
}
}

现在,您可能会发现一个限制,即当我使用构建器时,必须首先调用与子类相关的with...方法,然后链接超类的方法,这不是什么大问题,但仍不确定是否是良好的实践方法。另一方面,我可以将子类的with...方法全部添加到超类接口中,然后限制就消失了,但是我会得到一个包含不同子类的混合with...方法的接口。您更喜欢/建议哪一个?

ABuilder 中的 withS1() 返回什么? - alayor
2
这是一个非常常见的问题。一种模式是在超类上定义一个通用的“self type”,并由其方法返回。 - shmosel
这个或者这个这样的东西? - Roman Vottner
好的,看起来唯一的选择是使用泛型。我必须说,泛型会使它不那么用户友好,所以我可能需要坚持我的当前小限制。 - radio
这里的用户是谁?如果你指的是使用构建器的人,它非常用户友好。他甚至看不到任何泛型。 - shmosel
显示剩余2条评论
1个回答

5

修改超类的构造器,使用F-bound(也称为奇异递归模板模式)。

public interface SuperClassBuilderInterface<SELF extends SuperClassBuilderInterface<SELF>> {
    SELF withS1(String s1);
    // etc.
    Superclass build();
}

那么你有以下内容:
class SuperClassBuilder<SELF extends SuperClassBuilder<SELF>> implements SuperClassBuilderInterface<SELF>

interface ABuilderInterface<SELF extends ABuilderInterface<SELF>> extends SuperClassBuilderInterface<SELF>

class ABuilder extends SuperClassBuilder<ABuilder> implements ABuilderInterface<ABuilder>

请注意,SuperClassBuilder的实现必须包含以下形式的未经检查的强制类型转换:return (SELF)this;。理论上讲,类型系统足够强大,不需要这样做,但是结果的编码可能非常丑陋(参见此文献),并且可能不值得这样做。
注:这就是 @shmosel 的意思。

我刚试了一下,它可以正常工作,非常感谢。起初我不太喜欢它,因为它添加了很多泛型,而我一开始并不知道它们是如何工作的,需要再认真思考一下 :) 但对于调用者来说更容易,因为他将没有任何限制。 - radio
顺便问一下,这个正确吗?: SuperClassBuilder的实现返回SELF,如你所提到的,代码为return (SELF) this;, 而ABuilderInterface的方法返回SELF,其实现返回ABuilder。 - radio
是的。我实际上会编辑答案,以避免使用未经检查的转换。 - HTNW
糟糕,这是不可能的。几乎可能,但实际上并不行。 - HTNW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接