【问题标题】:Fluent interface - return most specific return type流畅的接口 - 返回最具体的返回类型
【发布时间】:2013-09-08 01:34:07
【问题描述】:

情况:

我有一个看起来有点像这样的类层次结构。请注意,我的真的看起来不是这样的,我只是这样写它以使其更短,并且在我看来更清晰。

public abstract BaseClass {
    private Alpha a;
    private Beta b;

    public Alpha getAlpha() { return a; }
    public Beta getBeta() { return b; }
    public BaseClass setAlpha(Alpha a) { this.a = a; return this; }
    public BaseClass setBeta(Beta b) { this.b = b; return this; }
}

public Foo extends BaseClass {
    @Override
    public Foo setBeta(Beta b) {
        super.setBeta(b);
        doSomethingElse();
        return this;
    }
    public Gamma getGamma() { return g; }
    public Foo setGamma(Gamma g) { this.g = g; return this; }
}

public Bar extends BaseClass {
    private Omega o;
    @Override
    public Bar setAlpha(Alpha a) {
        super.setAlpha();
        doSomethingElse();
        return this;
    }
    public Omega getOmega() { return o; }
    public Bar setOmega(Omega o) { this.o = o; return this; }
}

问题:

上面列出的这些方法还有很多。请注意,它们不符合相同的接口。使用FooBar 并且需要调用setGammasetOmega 的类始终可以访问最具体的类。但是,同样的类也需要调用setAlphasetBeta。但是,除非我重写它们,否则这些方法不会返回正确的类型。

选项:

  1. 重写BaseClass的每个方法只是为了改变返回类型
    • 似乎有很多不必要的代码
  2. BaseClass 中声明每个方法,如setGammasetOmega 抽象。这可能会奏效,除非我必须为完全没有意义的类定义它。
  3. 类似于#2,除了我没有声明这些方法抽象,我给他们一个实现 - throw new UnsupportedOperationException()
  4. 与 #3 类似,但我只是忽略了这个问题,而不是抛出 Exception
  5. 始终将我的所有构建器都放在最具体的返回类型之前。即someBar.setOmega().setAlpha() 但不是someBar.setAlpha().setOmega()

所有这些选项看起来都很恶心。有人有更好的吗?

注意:这种情况不太像这种情况:Java - Inherited Fluent method return type to return incident class' type, not parent's,因为它不是真正的泛型问题。

【问题讨论】:

  • 如果您需要在setBetasetAlpha 期间致电doSomethingElse(),您别无选择。
  • 我认为您可以将BaseClass 泛化并让setAlpha/Beta 返回<T>,但我今晚还没来得及提出签名。

标签: java design-patterns fluent return-type


【解决方案1】:

正如@chrylis 在 cmets 中指出的那样,您可以泛化 BaseClass

public abstract class BaseClass<T extends BaseClass> {
    private Alpha a;
    private Beta b;

    public Alpha getAlpha() {
        return a;
    }

    public Beta getBeta() {
        return b;
    }

    @SuppressWarnings("unchecked")
    public T setAlpha(Alpha a) {
        this.a = a;
        return (T) this;
    }

    @SuppressWarnings("unchecked")
    public T setBeta(Beta b) {
        this.b = b;
        return (T) this;
    }
}

并扩展如下:

富:

public class Foo extends BaseClass<Foo> {
    private Gamma g;

    @Override
    public Foo setBeta(Beta b) {
        super.setBeta(b);
        doSomethingElse();
        return this;
    }

    private void doSomethingElse() { }

    public Gamma getGamma() {
        return g;
    }

    public Foo setGamma(Gamma g) {
        this.g = g;
        return this;
    }
}

酒吧:

public class Bar extends BaseClass<Bar> {
    private Omega o;

    @Override
    public Bar setAlpha(Alpha a) {
        super.setAlpha(a);
        doSomethingElse();
        return this;
    }

    private void doSomethingElse() { }

    public Omega getOmega() {
        return o;
    }

    public Bar setOmega(Omega o) {
        this.o = o;
        return this;
    }
}

这引入了几个未经检查的强制转换警告,我将其抑制。它可以编译,但我还没有尝试用它实际做任何事情。

【讨论】:

  • 我有点喜欢这个。我看到的一个问题是从技术上讲你可以做public class Baz extends BaseClass&lt;Quux&gt;,这会导致ClassCastExceptions。我当然不会那样做,但它会让设计容易出现这种愚蠢的错误。
  • 是的,不可否认它有点脆弱。
  • 如果您的答案是选项#6,如果您正在设计这个系统,您会选择哪个选项?
  • 我可能会这样做。它并不完美,但我认为它比其他选择更好。
  • 如果你要走这条路,请使用基类泛型参数&lt;T extends BaseClass&lt;T&gt;&gt; 而不是`'。我还建议仅以受控方式使用这种风格,而不是将其应用于整个架构,因为由于泛型问题,Java 确实不能很好地处理流畅的接口。
猜你喜欢
  • 2011-02-10
  • 2016-03-15
  • 2016-05-31
  • 1970-01-01
  • 2018-06-07
  • 2019-05-10
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
相关资源
最近更新 更多