有问题
我一直在为我在 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 来实例化该类的每种不同方式,并且每组都被复制 4 次(一组用于为所有可设置的公共(public)属性)这 3 种方法)。
我想知道是否有一种方法可以减少复制粘贴的代码和文档的数量。理想情况下,我最终只会有 3 个构造函数,而不是 12 个。
最佳答案
当一个类有多个带有默认值的可选字段时,为了避免编写指数级数量的构造函数,您可以使用 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()
一样使用它来创建一个新对象给定初始值的字段,或省略一些链接方法以使用这些字段的默认值。
关于java - 重复的 Java 构造函数(重构或生成),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59338296/