Java Builder 模式和“深层”对象层次结构

2022-09-03 15:24:51

在“深层”对象层次结构中使用 Builder 模式的最佳做法是什么?为了详细说明,我探索了将Joshua Bloch提出的Builder模式应用于我的XML绑定代码的想法(我正在使用SimpleXML,但这个问题适用于任何情况)。我的对象层次结构有 4 个级别,具有不同程度的复杂程度。我的意思是,在某些级别中,我的对象只有几个属性,而在其他一些级别上,我最多有10个属性。

因此,请考虑这个假设的示例(为了简洁起见,我省略了简单的XML注释)

public class Outermost {

    private String title;
    private int channel;
    private List<Middle> middleList;

}

class Middle{
    private int id;
    private String name;
    private boolean senior;
    /* ... ... 10 such properties */

    private Innermost inner;
}

class Innermost{
    private String something;
    private int foo;
    /* ... Few more of these ..*/
}

如果我想使用构建器强制创建对象,那么最好的方法是什么?最明显的答案是为上述每个类设置类。Outermostinner static Builder

但是,这难道不会使事情变得像 Builder 模式试图解决的问题一样笨拙吗?我正在考虑这样的事情 - 这将强制实施“由内而外”的方法 - 这意味着在将对象添加到对象之前,必须完全构造和实例化对象。但我们都知道,在实践中(特别是当一个人正在构建XML或JSON时),我们很少有“及时”的信息来实现这一目标。InnermostMiddle

很有可能,人们最终会为每个属性提供变量 - 跨越所有级别;并在最后创建对象。或者,最终将有代码中浮动多个级别的生成器,从而增加了混乱。

那么,关于如何优雅地实现这一点的任何想法?


答案 1

这里对生成器模式的描述是我猜你所指的;这与维基百科中描述的模式略有不同,我更喜欢前者。

我不认为你对构造顺序或封装丢失的担忧不可避免地来自我阅读的描述。对我来说,最大的问题是原始数据的结构。

假设我们有

 public OuterBuilder {
     // some outer attributes here

     private ArrayList<MiddleBuilder> m_middleList;

     public OuterBuild( mandatory params for Outers ){
          // populate some outer attributes
          // create empty middle array
     }

     public addMiddle(MiddleBuilder middler) {
              m_middleList.add(middler);
     } 
 }

现在我们可以根据需要创建任意数量的中间构建器

 while (middleDataIter.hasNext() ) {
      MiddleData data = middleDateIter.next();
      // make a middle builder, add it.
 }

我们可以将相同的模式应用于更深层次的嵌套。

为了解决您的第一点,每个属性的变量:取决于我们如何设计建筑商以及我们的数据来自哪里。如果我们来自UI,那么无论如何,我们几乎每个属性都有一个变量,我们的情况并不差。如果按照我上面的建议,我们正在迭代一些数据结构,那么构建者可能负责迭代该数据结构。在我的示例中,我们将 MiddleData 实例向下传递。一些额外的耦合,但它确实封装了细节。

为了解决您的第二个问题,我们不会在进行构建时构建事物,而是有效地使用构建器作为数据的累积点。最终,我们调用“Go and Build”方法,但此时我们应该将所有数据都放在适当的位置,以便整个层次结构就构建。


答案 2

这是可以做到的,但可以说是不值得做的。显而易见的实现...

class Shape
{
    private final double opacity;

    public double getOpacity()
    {
        return opacity;
    }

    public static abstract class Builder<T extends Shape> {

        private double opacity;

        public Builder<T> opacity(double opacity) {
            this.opacity = opacity;
            return this;
        }

        public abstract T build();
    }

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

    protected Shape(Builder<?> builder) {
        this.opacity = builder.opacity;
    }
}

class Rectangle extends Shape {

    private final double height;
    private final double width;

    public double getHeight()
    {
        return height;
    }

    public double getWidth()
    {
        return width;
    }

    public static abstract class Builder<T extends Rectangle> extends Shape.Builder<T> {
        private double height;
        private double width;

        public Builder<T> height(double height) {
            this.height = height;
            return this;
        }

        public Builder<T> width(double width) {
            this.width = width;
            return this;
        }
    }

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

    protected Rectangle(Builder<?> builder) {
        super(builder);
        this.height = builder.height;
        this.width = builder.width;
    }
}

...很快遇到问题。如果你尝试类似的东西

Rectangle r = Rectangle.builder().opacity(0.5).height(50).width(100).build();

它不会编译,因为不知道它返回的是 a,只是一个 .因此,您必须按顺序调用属性,从最派生到最不派生:opacity()Rectangle.BuilderShape.Builder<Rectangle>

Rectangle r = Rectangle.builder().height(50).width(100).opacity(0.5).build();

如果要解决此问题,则需要使属性方法泛型,以便超类方法仍将返回子类生成器。AFAIK没有办法使这个100%可靠,但是通过一些自我引用的泛型,你可以接近:

class Shape
{
    private final double opacity;

    public double getOpacity ()
    {
        return opacity;
    }

    public static abstract class ShapeBuilder<S extends Shape, B extends ShapeBuilder<S, B>>
    {

        private double opacity;

        @SuppressWarnings( "unchecked" )
        public B opacity ( double opacity )
        {
            this.opacity = opacity;
            return (B) this;
        }

        public abstract S build ();
    }

    private static class DefaultShapeBuilder extends ShapeBuilder<Shape, DefaultShapeBuilder>
    {
        @Override
        public Shape build ()
        {
            return new Shape( this );
        }
    }

    public static ShapeBuilder<?, ?> builder ()
    {
        return new DefaultShapeBuilder();
    }

    protected Shape ( ShapeBuilder<?, ?> builder )
    {
        this.opacity = builder.opacity;
    }
}

class Rectangle extends Shape
{

    private final double height;
    private final double width;

    public double getHeight ()
    {
        return height;
    }

    public double getWidth ()
    {
        return width;
    }

    public static abstract class RectangleBuilder<S extends Rectangle, B extends RectangleBuilder<S, B>> extends ShapeBuilder<S, B>
    {
        private double height;
        private double width;

        @SuppressWarnings( "unchecked" )
        public B height ( double height )
        {
            this.height = height;
            return (B) this;
        }

        @SuppressWarnings( "unchecked" )
        public B width ( double width )
        {
            this.width = width;
            return (B) this;
        }
    }

    public static RectangleBuilder<?, ?> builder ()
    {
        return new DefaultRectangleBuilder();
    }

    protected Rectangle ( RectangleBuilder<?, ?> builder )
    {
        super( builder );
        this.height = builder.height;
        this.width = builder.width;
    }

    private static class DefaultRectangleBuilder extends RectangleBuilder<Rectangle, DefaultRectangleBuilder>
    {
        @Override
        public Rectangle build ()
        {
            return new Rectangle( this );
        }
    }
}

class RotatedRectangle extends Rectangle
{
    private final double theta;

    public double getTheta ()
    {
        return theta;
    }

    public static abstract class RotatedRectangleBuilder<S extends RotatedRectangle, B extends RotatedRectangleBuilder<S, B>> extends Rectangle.RectangleBuilder<S, B>
    {
        private double theta;

        @SuppressWarnings( "Unchecked" )
        public B theta ( double theta )
        {
            this.theta = theta;
            return (B) this;
        }
    }

    public static RotatedRectangleBuilder<?, ?> builder ()
    {
        return new DefaultRotatedRectangleBuilder();
    }

    protected RotatedRectangle ( RotatedRectangleBuilder<?, ?> builder )
    {
        super( builder );
        this.theta = builder.theta;
    }

    private static class DefaultRotatedRectangleBuilder extends RotatedRectangleBuilder<RotatedRectangle, DefaultRotatedRectangleBuilder>
    {
        @Override
        public RotatedRectangle build ()
        {
            return new RotatedRectangle( this );
        }
    }
}

class BuilderTest
{
    public static void main ( String[] args )
    {
        RotatedRectangle rotatedRectangle = RotatedRectangle.builder()
                .theta( Math.PI / 2 )
                .width( 640 )
                .height( 400 )
                .height( 400 )
                .opacity( 0.5d ) // note attribs can be set in any order
                .width( 111 )
                .opacity( 0.5d )
                .width( 222 )
                .height( 400 )
                .width( 640 )
                .width( 640 )
                .build();
        System.out.println( rotatedRectangle.getTheta() );
        System.out.println( rotatedRectangle.getWidth() );
        System.out.println( rotatedRectangle.getHeight() );
        System.out.println( rotatedRectangle.getOpacity() );
    }
}

请注意注释;如果一个子类打破了总是扩展的约定,系统就会崩溃。@SuppressWarningsFooBuilderFooSuperclassBuilder<Foo, FooBuilder>

你可以看到代码变得多么丑陋。在这一点上,也许最好放弃第2项,转而冥想第16项:支持作文而不是继承


推荐