重复的Java构造函数(重构或生成)

问题描述 投票:1回答:1

问题

我一直在为我在Java中使用的框架创建一个库:我有数百个LOC纯粹用于为用户提供许多构造函数,但是每次都使用一个较少的参数(因为默认值可以是用过的)。这导致了很多重复的代码和很多重复的JavaDoc。而且,最重要的是,每当一件小事情发生变化时,都会进行大量维护。


解决方案示例

我觉得那里可能存在一些类似于Lombok的库,该库使我可以以自动生成构造函数及其关联的JavaDoc的方式来注释构造函数。类似于:

/** Some description. */
public class Example {    

    /** Alternate1 description. */
    public SomeObject alternate1;

    /** Alternate2 description. */
    public SomeOtherObject alternate2;

    /** Attribute1 description. */
    public int attribute1 = 1;

    /** Attribute2 description. */
    public int attribute2 = 2;

    @InjectClassDoc
    @InjectAttributesDoc
    public Example (@ConstructorRequiredVariant SomeObject obj,
            @ConstructorOrderedCascade int attribute1,
            @ConstructorOrderedCascade int attribute2) {
        doSomething();
    }

    @InjectClassDoc
    @InjectAttributesDoc
    public Example (@ConstructorRequiredVariant SomeOtherObject obj,
            @ConstructorOrderedCascade int attribute1,
            @ConstructorOrderedCascade int attribute2) {
        doSomething();
    }

    // getters and setters

}

这将产生类似的内容:

/** Some description. */
public class Example {    

    /** Alternate1 description. */
    public SomeObject someObject;

    /** Alternate2 description. */
    public SomeOtherObject someOtherObject;

    /** Attribute1 description. */
    public int attribute1 = 1;

    /** Attribute2 description. */
    public int attribute2 = 2;

    /**
     * Some description.
     * @param obj Alternate1 description.
     * @param attribute1 Attribute1 description.
     * @param attribute2 Attribute2 description.
     */
    public Example (final SomeObject obj,
            final int attribute1,
            final int attribute2) {
        this(obj, attribute1);
        setAttribute2(attribute2);
    }

    /**
     * Some description.
     * @param obj Alternate1 description.
     * @param attribute1 Attribute1 description.
     */
    public Example (final SomeObject obj,
            final int attribute1) {
        this(obj);
        setAttribute1(attribute1);
    }

    /**
     * Some description.
     * @param obj Alternate1 description.
     */
    public Example (final SomeObject obj) {
        setSomeObject(obj);
        doSomething();
    }

    /**
     * Some description.
     * @param obj Alternate2 description.
     * @param attribute1 Attribute1 description.
     * @param attribute2 Attribute2 description.
     */
    public Example (final SomeOtherObject obj,
            final int attribute1,
            final int attribute2) {
        this(obj, attribute1);
        setAttribute2(attribute2);
    }

    /**
     * Some description.
     * @param obj Alternate2 description.
     * @param attribute1 Attribute1 description.
     */
    public Example (final SomeOtherObject obj,
            final int attribute1) {
        this(obj);
        setAttribute1(attribute1);
    }

    /**
     * Some description.
     * @param obj Alternate2 description.
     */
    public Example (final SomeOtherObject obj) {
        setSomeOtherObject(obj);
        doSomething();
    }

    // getters and setters

}

问题的实际例子

这只是为了让您更好地了解我来自何处。

这里是我的问题根源摘录(link to the actual repo's source):

/** Some description that is reused on all constructors seen by the users. */
public class PieWidget extends RadialGroup {

    /** Used internally for the shared properties among constructors of RadialWidgets. */
    protected void constructorsCommon(TextureRegion whitePixel) {
        this.whitePixel = whitePixel;
    }


    /** Used internally for the shared properties among constructors of RadialWidgets. */
    protected PieWidget(final TextureRegion whitePixel, float preferredRadius) {
        super(preferredRadius);
        constructorsCommon(whitePixel);
    }


    /** Used internally for the shared properties among constructors of RadialWidgets. */
    protected PieWidget(final TextureRegion whitePixel,
                        float preferredRadius, float innerRadiusPercent) {
        super(preferredRadius, innerRadiusPercent);
        constructorsCommon(whitePixel);
    }


    /** Used internally for the shared properties among constructors of RadialWidgets. */
    protected PieWidget(final TextureRegion whitePixel,
                        float preferredRadius, float innerRadiusPercent, float startDegreesOffset) {
        super(preferredRadius, innerRadiusPercent, startDegreesOffset);
        constructorsCommon(whitePixel);
    }


    /** Used internally for the shared properties among constructors of RadialWidgets. */
    protected PieWidget(final TextureRegion whitePixel,
                        float preferredRadius, float innerRadiusPercent,
                        float startDegreesOffset, float totalDegreesDrawn) {
        super(preferredRadius, innerRadiusPercent, startDegreesOffset, totalDegreesDrawn);
        constructorsCommon(whitePixel);
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param style defines the way the widget looks like.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     */
    public PieWidget(final TextureRegion whitePixel,
                     PieWidgetStyle style, float preferredRadius) {
        this(whitePixel, preferredRadius);
        setStyle(style);
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param style defines the way the widget looks like.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     */
    public PieWidget(final TextureRegion whitePixel,
                     PieWidgetStyle style, float preferredRadius,
                     float innerRadiusPercent) {
        this(whitePixel, preferredRadius, innerRadiusPercent);
        setStyle(style);
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param style defines the way the widget looks like.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     * @param startDegreesOffset the {@link #startDegreesOffset} that defines
     *                           how far from the origin the drawing begins.
     */
    public PieWidget(final TextureRegion whitePixel,
                     PieWidgetStyle style, float preferredRadius,
                     float innerRadiusPercent, float startDegreesOffset) {
        this(whitePixel, preferredRadius, innerRadiusPercent, startDegreesOffset);
        setStyle(style);
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param style defines the way the widget looks like.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     * @param startDegreesOffset the {@link #startDegreesOffset} that defines
     *                           how far from the origin the drawing begins.
     * @param totalDegreesDrawn the {@link #totalDegreesDrawn} that defines how
     *                          many degrees the widget will span, starting from
     *                          its {@link #startDegreesOffset}.
     */
    public PieWidget(final TextureRegion whitePixel,
                     PieWidgetStyle style, float preferredRadius,
                     float innerRadiusPercent, float startDegreesOffset, float totalDegreesDrawn) {
        this(whitePixel, preferredRadius, innerRadiusPercent, startDegreesOffset, totalDegreesDrawn);
        setStyle(style);
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param skin defines the way the widget looks like.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     */
    public PieWidget(final TextureRegion whitePixel,
                     Skin skin, float preferredRadius) {
        this(whitePixel, preferredRadius);
        setStyle(skin.get(PieWidgetStyle.class));
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param skin defines the way the widget looks like.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     */
    public PieWidget(final TextureRegion whitePixel,
                     Skin skin, float preferredRadius,
                     float innerRadiusPercent) {
        this(whitePixel, preferredRadius, innerRadiusPercent);
        setStyle(skin.get(PieWidgetStyle.class));
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param skin defines the way the widget looks like.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     * @param startDegreesOffset the {@link #startDegreesOffset} that defines
     *                           how far from the origin the drawing begins.
     */
    public PieWidget(final TextureRegion whitePixel,
                     Skin skin, float preferredRadius,
                     float innerRadiusPercent, float startDegreesOffset) {
        this(whitePixel, preferredRadius, innerRadiusPercent, startDegreesOffset);
        setStyle(skin.get(PieWidgetStyle.class));
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param skin defines the way the widget looks like.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     * @param startDegreesOffset the {@link #startDegreesOffset} that defines
     *                           how far from the origin the drawing begins.
     * @param totalDegreesDrawn the {@link #totalDegreesDrawn} that defines how
     *                          many degrees the widget will span, starting from
     *                          its {@link #startDegreesOffset}.
     */
    public PieWidget(final TextureRegion whitePixel,
                     Skin skin, float preferredRadius,
                     float innerRadiusPercent, float startDegreesOffset, float totalDegreesDrawn) {
        this(whitePixel, preferredRadius, innerRadiusPercent, startDegreesOffset, totalDegreesDrawn);
        setStyle(skin.get(PieWidgetStyle.class));
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param skin defines the way the widget looks like.
     * @param style the name of the style to be extracted from the skin.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     */
    public PieWidget(final TextureRegion whitePixel,
                     Skin skin, String style, float preferredRadius) {
        this(whitePixel, preferredRadius);
        setStyle(skin.get(style, PieWidgetStyle.class));
    }




    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param skin defines the way the widget looks like.
     * @param style the name of the style to be extracted from the skin.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     */
    public PieWidget(final TextureRegion whitePixel,
                     Skin skin, String style, float preferredRadius,
                     float innerRadiusPercent) {
        this(whitePixel, preferredRadius, innerRadiusPercent);
        setStyle(skin.get(style, PieWidgetStyle.class));
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param skin defines the way the widget looks like.
     * @param style the name of the style to be extracted from the skin.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     * @param startDegreesOffset the {@link #startDegreesOffset} that defines
     *                           how far from the origin the drawing begins.
     */
    public PieWidget(final TextureRegion whitePixel,
                     Skin skin, String style, float preferredRadius,
                     float innerRadiusPercent, float startDegreesOffset) {
        this(whitePixel, preferredRadius, innerRadiusPercent, startDegreesOffset);
        setStyle(skin.get(style, PieWidgetStyle.class));
    }


    /**
     * See {@link PieWidget} for a description.
     *
     * @param whitePixel a 1x1 white pixel.
     * @param skin defines the way the widget looks like.
     * @param style the name of the style to be extracted from the skin.
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     * @param startDegreesOffset the {@link #startDegreesOffset} that defines
     *                           how far from the origin the drawing begins.
     * @param totalDegreesDrawn the {@link #totalDegreesDrawn} that defines how
     *                          many degrees the widget will span, starting from
     *                          its {@link #startDegreesOffset}.
     */
    public PieWidget(final TextureRegion whitePixel,
                     Skin skin, String style, float preferredRadius,
                     float innerRadiusPercent, float startDegreesOffset, float totalDegreesDrawn) {
        this(whitePixel, preferredRadius, innerRadiusPercent, startDegreesOffset, totalDegreesDrawn);
        setStyle(skin.get(style, PieWidgetStyle.class));
    }

    // ... then the actual methods start
}

父类:

public class RadialGroup extends WidgetGroup {

    /** Used internally for the shared properties among constructors of RadialWidgets. */
    protected void constructorsCommon() {
        setTouchable(Touchable.childrenOnly);
    }


    /**
     * See {@link RadialGroup} for a description.
     *
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     */
    public RadialGroup(float preferredRadius) {
        setPreferredRadius(preferredRadius);
        constructorsCommon();
    }

    /**
     * See {@link RadialGroup} for a description.
     *
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     */
    public RadialGroup(float preferredRadius, float innerRadiusPercent) {
        this(preferredRadius);
        setInnerRadiusPercent(innerRadiusPercent);
    }

    /**
     * See {@link RadialGroup} for a description.
     *
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     * @param startDegreesOffset the {@link #startDegreesOffset} that defines
     *                           how far from the origin the drawing begins.
     */
    public RadialGroup(float preferredRadius, float innerRadiusPercent,
                       float startDegreesOffset) {
        this(preferredRadius, innerRadiusPercent);
        setStartDegreesOffset(startDegreesOffset);
    }

    /**
     * See {@link RadialGroup} for a description.
     *
     * @param preferredRadius the {@link #preferredRadius} that defines the 
     *                        size of the widget.
     * @param innerRadiusPercent the {@link #innerRadiusPercent} that defines
     *                           the percentage of the radius that is cut off,
     *                           starting from the center of the widget.
     * @param startDegreesOffset the {@link #startDegreesOffset} that defines
     *                           how far from the origin the drawing begins.
     * @param totalDegreesDrawn the {@link #totalDegreesDrawn} that defines how
     *                          many degrees the widget will span, starting from
     *                          its {@link #startDegreesOffset}.
     */
    public RadialGroup(float preferredRadius, float innerRadiusPercent,
                       float startDegreesOffset, float totalDegreesDrawn) {
        this(preferredRadius, innerRadiusPercent, startDegreesOffset);
        setTotalDegreesDrawn(totalDegreesDrawn);
    }

    // ...
}

结构很简单:

  • [constructorsCommon()方法用于对使用的任何构造方法进行通用操作。
  • 构造函数本身也非常琐碎:它们使用与类的单个字段关联的setter,然后将调用传递给定义了较少属性的另一个构造函数。
  • 该类有3组构造函数:一组用于提供实例化Skin的Skin的每种不同方式,每组都被复制4次(一组针对可以为所有这些属性设置的每个通用属性) 3种方法)。

我想知道是否有办法减少复制粘贴的代码和文档的数量。理想情况下,我将只剩下3个构造函数,而不是12个。

java refactoring code-generation documentation-generation
1个回答
1
投票

为了避免在类具有多个具有默认值的可选字段时以指数方式编写许多构造函数,可以使用Builder pattern。例如:

public class Example {
    private int a;
    private String b;
    private char c;
    private boolean d;

    private Example(int a, String b, char c, boolean d) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
    }

    public static class Builder {
        // required fields
        private int a;
        private String b;

        // optional fields with default values
        private char c = 'c';
        private boolean d = false;

        public Builder(int a, String b) {
            this.a = a;
            this.b = b;
        }

        public Builder c(char c) {
            this.c = c;
            return this;
        }

        public Builder d(boolean d) {
            this.d = d;
            return this;
        }

        public Example build() {
            return new Example(a, b, c, d);
        }
    }
}

然后,您可以像new Example.Builder(23, "foo").c('y').d(false).build()那样使用它来创建一个新对象,并为每个字段指定一个初始值,或者省略某些链接方法以使用这些字段的默认值。

© www.soinside.com 2019 - 2024. All rights reserved.