且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

生成器模式和继承

更新时间:2022-10-17 17:58:23

这是肯定可以用递归泛型,但你需要将通用类型全部传递下来。语法有点麻烦,但你一定可以这样做。

 抽象类GenericMammalBuilder //将此扩展为
&lt ; B扩展GenericMammalBuilder< B>> {// Mammal subtype builders

String sex;
字符串名称;

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

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

final class MammalBuilder //使用this to
extends GenericMammalBuilder< MammalBuilder> {//构建新的哺乳动物实例
Mammal build(){
return new Mammal(this);
}
}

抽象类GenericRabbitBuilder //将此扩展为
< B扩展GenericRabbitBuilder< B>> // Rabbit子类型构建器
扩展GenericMammalBuilder< B> {//例如LopBuilder

颜色颜色;

B颜色(颜色){
this.color = color;
return(B)this;
}
}

final class RabbitBuilder //使用这个来构建
扩展GenericRabbitBuilder< RabbitBuilder> {//新的兔子实例
Rabbit build(){
return new Rabbit(this);
}
}

递归泛型的问题是参数必须参数化子类型IE如果您有:

 类A< T扩展A&T;> {} 

class B< T extends B< T>延伸A< T> {}

类C扩展B< C> {}

您可以执行 new C(); 而不是新的B< B>(); 。 (错误地,你也可以做新的B< C>(); 。)



所以你需要一些一种临时具体课。这是一个选择你自己的恶意场景IMO。


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

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);

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.

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.

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;
}

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 generic but you need to be passing the generic type all way down. The syntax is a little cumbersome but you can certainly do it.

abstract class GenericMammalBuilder     // extend this for
<B extends GenericMammalBuilder<B>> {   // Mammal subtype builders

     String sex;
     String name;

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

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

final class MammalBuilder                       // use this to
extends GenericMammalBuilder<MammalBuilder> {   // build new Mammal instances
    Mammal build() {
        return new Mammal(this);
    }
}

abstract class GenericRabbitBuilder   // extend this for
<B extends GenericRabbitBuilder<B>>   // Rabbit subtype builders
extends GenericMammalBuilder<B> {     // e.g. LopBuilder

    Color color;

    B ofColor(Color color) {
        this.color = color;
        return (B)this;
    }
}

final class RabbitBuilder                       // use this to build
extends GenericRabbitBuilder<RabbitBuilder> {   // new Rabbit instances
    Rabbit build() {
        return new Rabbit(this);
    }
}

The problem with the recursive generic is the parameter must be a parameterized subtype IE if you have:

class A<T extends A<T>> {}

class B<T extends B<T>> extends A<T> {}

class C extends B<C> {}

You can do new C(); but not new B<B>();. (Erroneously, you can also do new B<C>();.)

So you do need some kind of an "interim concrete" class. This is sortof a choose your own evil scenario IMO.