构建器模式和继承 [英] Builder Pattern and Inheritance

查看:34
本文介绍了构建器模式和继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个对象层次结构,随着继承树的加深,它的复杂性也会增加.这些都不是抽象的,因此,它们的所有实例都服务于或多或少复杂的目的.

I have an object hierarchy that increases in complexity as the inheritance tree deepens. None of these are abstract, hence, all of their instances serve a, more or less sophisticated, purpose.

由于参数数量相当多,我想使用构建器模式来设置属性而不是编写多个构造函数.由于我需要满足所有排列,因此我的继承树中的叶类将具有伸缩构造函数.

As the number of parameters is quite high, I would want to use the Builder Pattern to set properties rather than code several constructors. As I need to cater to all permutations, leaf classes in my inheritance tree would have telescoping constructors.

当我在设计过程中遇到一些问题时,我在这里浏览了一个答案.首先,让我给你一个简单、浅显的例子来说明问题.

I have browsed for an answer here when I hit some problems during my design. First of, let me give you a simple, shallow example to illustrate the problem.

public class Rabbit
{
    public String sex;
    public String name;

    public Rabbit(Builder builder)
    {
        sex = builder.sex;
        name = builder.name;
    }

    public static class Builder
    {
        protected String sex;
        protected String name;

        public Builder() { }

        public Builder sex(String sex)
        {
            this.sex = sex;
            return this;
        }

        public Builder name(String name)
        {
            this.name = name;
            return this;
        }

        public Rabbit build()
        {
            return new Rabbit(this);
        }
    }
}

public class Lop extends Rabbit
{
    public float earLength;
    public String furColour;

    public Lop(LopBuilder builder)
    {
        super(builder);
        this.earLength = builder.earLength;
        this.furColour = builder.furColour;
    }

    public static class LopBuilder extends Rabbit.Builder
    {
        protected float earLength;
        protected String furColour;

        public LopBuilder() { }

        public Builder earLength(float length)
        {
            this.earLength = length;
            return this;
        }

        public Builder furColour(String colour)
        {
            this.furColour = colour;
            return this;
        }

        public Lop build()
        {
            return new Lop(this);
        }
    }
}

现在我们有一些代码要做,我想构建一个Lop:

Now that we have some code to go on, imaging I want to build a Lop:

Lop lop = new Lop.LopBuilder().furColour("Gray").name("Rabbit").earLength(4.6f);

这个调用不会编译,因为最后一个链式调用无法解析,Builder 没有定义方法 earLength.所以这种方式要求所有调用都按照特定的顺序链接起来,这是非常不切实际的,尤其是对于一个很深的层次树.

This call will not compile as the last chained call cannot be resolved, Builder not defining the method earLength. So this way requires that all calls be chained in a specific order which is very impractical, especially with a deep hierarchy tree.

现在,在我寻找答案的过程中,我遇到了继承 Java Builder 类 建议使用 Curious Recursive Generic Pattern.但是,由于我的层次结构不包含抽象类,因此此解决方案对我不起作用.但是这种方法依赖于抽象和多态来发挥作用,这就是为什么我不相信我可以根据自己的需要调整它.

Now, during my search for an answer, I came across Subclassing a Java Builder class which suggests using the Curiously Recursive Generic Pattern. However, as my hierarchy does not contain an abstract class, this solution will not work for me. But the approach relies on abstraction and polymorphism to function which is why I don't believe I can adapt it to my needs.

我目前采用的一种方法是覆盖层次结构中超类 Builder 的所有方法,然后简单地执行以下操作:

An approach I have currently settled with is to override all methods of the superclass Builder in the hierarchy and simply do the following:

public ConcreteBuilder someOverridenMethod(Object someParameter)
{
    super(someParameter);
    return this;
}

通过这种方法,我可以保证我会返回一个可以发出链式调用的实例.虽然这并不像 Telescoping Anti-pattern 那样糟糕,但它紧随其后,我认为它有点hacky".​​

With this approach I can assure I am being returned an instance I can issue chain calls on. While this is not as worse as the Telescoping Anti-pattern, it is a close second and I consider it a bit "hacky".

是否有其他我不知道的问题的解决方案?最好是与设计模式一致的解决方案.谢谢!

Is there another solution to my problem that I am not aware of? Preferably a solution consistent with the design pattern. Thank you!

推荐答案

这对于递归绑定当然是可能的,但是子类型构建器也需要是泛型的,并且您需要一些临时抽象类.有点麻烦,但还是比非通用版本容易.

This is certainly possible with the recursive bound, but the subtype builders need to also be generic, and you need a few interim abstract classes. It's a little bit cumbersome, but it's still easier than the non-generic version.

/**
 * Extend this for Mammal subtype builders.
 */
abstract class GenericMammalBuilder<B extends GenericMammalBuilder<B>> {
    String sex;
    String name;

    B sex(String sex) {
        this.sex = sex;
        return self();
    }

    B name(String name) {
        this.name = name;
        return self();
    }

    abstract Mammal build();

    @SuppressWarnings("unchecked")
    final B self() {
        return (B) this;
    }
}

/**
 * Use this to actually build new Mammal instances.
 */
final class MammalBuilder extends GenericMammalBuilder<MammalBuilder> {
    @Override
    Mammal build() {
        return new Mammal(this);
    }
}

/**
 * Extend this for Rabbit subtype builders, e.g. LopBuilder.
 */
abstract class GenericRabbitBuilder<B extends GenericRabbitBuilder<B>>
        extends GenericMammalBuilder<B> {
    Color furColor;

    B furColor(Color furColor) {
        this.furColor = furColor;
        return self();
    }

    @Override
    abstract Rabbit build();
}

/**
 * Use this to actually build new Rabbit instances.
 */
final class RabbitBuilder extends GenericRabbitBuilder<RabbitBuilder> {
    @Override
    Rabbit build() {
        return new Rabbit(this);
    }
}

有一种方法可以避免使用具体"叶类,如果我们有这个:

There's a way to avoid having the "concrete" leaf classes, where if we had this:

class MammalBuilder<B extends MammalBuilder<B>> {
    ...
}
class RabbitBuilder<B extends RabbitBuilder<B>>
        extends MammalBuilder<B> {
    ...
}

然后需要用菱形新建实例,并在引用类型中使用通配符:

Then you need to create new instances with a diamond, and use wildcards in the reference type:

static RabbitBuilder<?> builder() {
    return new RabbitBuilder<>();
}

这是有效的,因为类型变量上的绑定确保了例如的所有方法RabbitBuilder 有一个带有 RabbitBuilder 的返回类型,即使类型参数只是一个通配符.

That works because the bound on the type variable ensures that all the methods of e.g. RabbitBuilder have a return type with RabbitBuilder, even when the type argument is just a wildcard.

不过,我不太喜欢这种方式,因为您需要在任何地方使用通配符,而且您只能使用菱形或 原始类型.我想你最终会有点尴尬.

I'm not much of a fan of that, though, because you need to use wildcards everywhere, and you can only create a new instance using the diamond or a raw type. I suppose you end up with a little awkwardness either way.

顺便说一下,关于这个:

And by the way, about this:

@SuppressWarnings("unchecked")
final B self() {
    return (B) this;
}

有一种方法可以避免未经检查的强制转换,即使方法抽象:

There's a way to avoid that unchecked cast, which is to make the method abstract:

abstract B self();

然后在叶子子类中覆盖它:

And then override it in the leaf subclass:

@Override
RabbitBuilder self() { return this; }

这样做的问题在于,虽然它更类型安全,但子类可以返回除 this 之外的其他内容.基本上,无论哪种方式,子类都有机会做错事,所以我真的看不出有什么理由更喜欢这些方法中的一种.

The issue with doing it that way is that although it's more type-safe, the subclass can return something other than this. Basically, either way, the subclass has the opportunity to do something wrong, so I don't really see much of a reason to prefer one of those approaches over the other.

这篇关于构建器模式和继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆