多态对象层次结构的生成器模式:Java 是否可能?

2022-09-01 19:01:44

我有一个接口的层次结构,实现.我想使用不可变对象,因此我想设计方便地构造这些对象的类。但是,我有很多接口,我不想在每种类型的子生成器中重复构建s的代码。ChildParentBuilderChildParent

因此,假设以下定义:

public interface Parent {
    public Long getParentProperty();
}

public interface Child1 extends Parent {
    public Integer getChild1Property(); 
}

public interface Child2 extends Parent {
    public String getChild2PropertyA();
    public Object getChild2PropertyB();
}

如何有效地实现构建器和?它们应支持如下操作:Child1BuilderChild2Builder

Child1 child1 = Child1Builder.newChild1().withChild1Property(5).withParentProperty(10L);

Child2 child2 = Child2Builder.newChild2().withChild2PropertyA("Hello").withParentProperty(10L).withChild2PropertyB(new Object());

我不想为每个儿童构建器实现一个特殊情况。withParentProperty

已编辑以添加第二个属性,以阐明这不能使用简单的泛型完成。我不是在寻找一种组合的方法,我正在寻找一种方法来实现一个系统,该系统不会重复为每个子类构建父类的工作。Child2Child1Child2Builder

感谢您的任何帮助!


答案 1

我想象的解决方案就像奇怪的重复模板模式,或CRTP。您可以定义一个基类来处理与父级相关的初始化,但您仍然可能会发现这两个样板和方法在每个派生的子相关生成器类中重复过多。getParent()getThis()

看一看:

abstract class ParentBase implements Parent
{
  @Override
  public final Long getParentProperty()
  {
      return parentProperty_;
  }


  protected void setParentProperty(Long value)
  {
      parentProperty_ = value;
  }


  private Long parentProperty_;
}


abstract class ParentBuilder<T extends ParentBuilder<T>>
{
  T withParentProperty(Long value)
  {
      getParent().setParentProperty(value);
      return getThis();
  }


  protected abstract ParentBase getParent();


  protected abstract T getThis();
}


final class ConcreteChild1 extends ParentBase implements Child1
{
  @Override
  public Integer getChild1Property()
  {
      return childProperty_;
  }


  public void setChild1Property(Integer value)
  {
      childProperty_ = value;
  }


  private Integer childProperty_;
}


final class Child1Builder extends ParentBuilder<Child1Builder>
{
  public Child1Builder()
  {
     pending_ = new ConcreteChild1();
  }


  public Child1Builder withChild1Property(Integer value)
  {
      pending_.setChild1Property(value);
      return this;
  }


  @Override
  protected ParentBase getParent()
  {
      return pending_;
  }


  @Override
  protected Child1Builder getThis()
  {
      return this;
  }


  private final ConcreteChild1 pending_;
}

如您所见,该类型期望与派生类型合作,以允许它返回正确类型的实例。它自己的引用不会到期,因为内部的类型当然是,而不是,比如说,为了保持“流利”的调用链接。ParentBuilderthisthisParentBuilderParentBuilderChild1Builder

我把“把戏”归功于Angelika Langer的教程条目getThis()


答案 2

我不认为而且是必要的,如果你愿意接受这些方法从“最年轻”到“最老”的限制:getParent()getThis()withXXXProperty()

class Parent
{
    private final long parentProperty;

    public long getParentProperty()
    {
        return parentProperty;
    }

    public static abstract class Builder<T extends Parent>
    {
        private long parentProperty;

        public Builder<T> withParentProperty( long parentProperty )
        {
            this.parentProperty = parentProperty;
            return this;
        }

        public abstract T build();
    }

    public static Builder<?> builder()
    {
        return new Builder<Parent>()
        {
            @Override
            public Parent build()
            {
                return new Parent(this);
            }
        };
    }

    protected Parent( Builder<?> builder )
    {
        this.parentProperty = builder.parentProperty;
    }
}

class Child1 extends Parent
{
    private final int child1Property;

    public int getChild1Property()
    {
        return child1Property;
    }

    public static abstract class Builder<T extends Child1> extends Parent.Builder<T>
    {
        private int child1Property;

        public Builder<T> withChild1Property( int child1Property )
        {
            this.child1Property = child1Property;
            return this;
        }

        public abstract T build();
    }

    public static Builder<?> builder()
    {
        return new Builder<Child1>()
        {
            @Override
            public Child1 build()
            {
                return new Child1(this);
            }
        };
    }

    protected Child1( Builder<?> builder )
    {
        super(builder);
        this.child1Property = builder.child1Property;
    }

}

class Child2 extends Parent
{

    private final String child2PropertyA;
    private final Object child2PropertyB;

    public String getChild2PropertyA()
    {
        return child2PropertyA;
    }

    public Object getChild2PropertyB()
    {
        return child2PropertyB;
    }

    public static abstract class Builder<T extends Child2> extends Parent.Builder<T>
    {
        private String child2PropertyA;
        private Object child2PropertyB;

        public Builder<T> withChild2PropertyA( String child2PropertyA )
        {
            this.child2PropertyA = child2PropertyA;
            return this;
        }

        public Builder<T> withChild2PropertyB( Object child2PropertyB )
        {
            this.child2PropertyB = child2PropertyB;
            return this;
        }
    }

    public static Builder<?> builder()
    {
        return new Builder<Child2>()
        {
            @Override
            public Child2 build()
            {
                return new Child2(this);
            }
        };
    }

    protected Child2( Builder<?> builder )
    {
        super(builder);
        this.child2PropertyA = builder.child2PropertyA;
        this.child2PropertyB = builder.child2PropertyB;
    }
}

class BuilderTest
{
    public static void main( String[] args )
    {
        Child1 child1 = Child1.builder()
                .withChild1Property(-3)
                .withParentProperty(5L)
                .build();

        Child2 grandchild = Child2.builder()
                .withChild2PropertyA("hello")
                .withChild2PropertyB(new Object())
                .withParentProperty(10L)
                .build();
    }
}

这里仍然有一些样板:每个方法中的匿名具体,以及每个构造函数中的调用。(注意:这假设每个级别都是为进一步的可继承性而设计的。如果在任何时候您有后代,则可以将生成器类具体化,并使构造函数私有。Builderbuilder()super()final

但我认为这个版本更容易遵循,对于下一个程序员来说,他们必须维护你的代码(对于初学者来说,没有自我引用的泛型;构建)。恕我直言,要求在父属性之前在构建器上设置子属性在一致性方面既是优势,也是灵活性方面的劣势。Builder<X>Xs


推荐