【问题标题】:How to document a Builder with its methods using JavaDoc如何使用 JavaDoc 记录 Builder 及其方法
【发布时间】:2022-08-22 00:34:25
【问题描述】:

我喜欢使用 Builder 模式来实例化具有复杂状态的类的对象,而不是使用太多的构造函数参数。

我可以将 JavaDoc 添加到类和每个单独的方法中,但我所知道的 JavaDoc 关键字似乎都不适合记录构建器的特殊性,例如哪些设置是强制性的,可选设置的默认值是什么。

如果我为每个单独的方法记录强制或可选和默认值,感觉就像文档传播得太多而无法全面了解它。如果我只记录最终的 build() 方法,告诉它何时可以使用默认值构建实例,以及何时不能。

这些选项似乎都不是真正令人满意的。感觉 JavaDoc 不太适合构建器模式,而是为遗留的面向对象代码风格设计的;这个或者我理解的不够好。

我搜索了https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html 文档,但找不到使用正确标签记录生成器的答案或指南。

@param 看起来像是一个有效的候选人,可以在 Builder 类本身这样的地方记录来自构建器的所有 setFoowithBaraddBaz,但它似乎不适合这种用法。

如何在 JavaDoc 或其他更合适的工具中正确记录 Builder?

  • 用一块石头解决 2 个问题: 问题 1:一般来说,现在 cmets 形式的文档是一种反模式——你应该把事情命名得足够好,以至于它们不需要文档。罕见的例外是方法契约的特殊细微差别。问题2:不要写任何代码!解决方案:将 Lombok 的 @Builder 注释添加到您的类中,您就完成了!
  • 没有用于记录构建器的任何特定 Javadoc 标记,至少默认 doclet 没有提供。您可以定义自己的标签,但这可能比它的价值更多。我会查看其他构建器是如何记录的(在 JDK 和第三方库中),然后使用您最喜欢的方法。
  • @Bohemian我恭敬但强烈不同意。方法名称不能告诉其他开发人员对参数和返回类型的约束。方法名称无法解释引发特定异常的原因。方法名称不能扩展行业特定术语的含义。在我看来,龙目岛是有毒垃圾;它在许多方面与面向对象开发相反,我无法一一列举。
  • @slaw 如果方法名称足以暗示其合同,请不要添加 cmets/doc。例如Optional<Post> getMostRecentPost(int userId) throws NoSuchUserException 不需要任何文档或解释。如果一段代码需要 cmets,那么将代码块分解成自己的方法并用简洁版本的 cmets 命名是一​​个很大的危险信号。顺便说一句,如果某些 cmets 由于无法通过更好的命名来解决的特殊性而并非绝对需要,我会拒绝 PR。加上我的团队已经很多年没有部署任何错误,所以该模式有效。
  • @slaw 这只是一个例子。至少该方法应该需要最少的 cmets - 也就是说,什么能够好的命名应该是暗示的(像findFirstPostExceptThoseMadeOnNewYearsEve() 这样的方法名称显然很荒谬)。无论剩下什么不能简单地通过良好的命名来暗示,都值得评论。见POLA

标签: java javadoc builder


【解决方案1】:

已经是你的第一个假设(“我能够将 JavaDoc 添加到类和每个单独的方法,[…]") 是错误的。

文档仍然是强制性的,并且应该尽可能靠近源。 JavaDoc 注释是部分来源,所以它不能更接近!

现在人们告诉你 cmets 是一种“反模式”,他们没有头绪,或者懒得打字,或者两者兼而有之——当你阅读那个“反模式”废话的源代码时,你会发现它在谈论排队cmets 解释你的代码中发生了什么,但不是关于可外化的cmets出于文档目的——因此与 JavaDoc 或 Doxygen cmets 无关(或相应工具在其他语言中的命名方式)。

对于您的问题:构建器类也将像任何其他类一样被记录,其中包含 JavaDoc 提供的关键字和适当的描述性文本。如果您懒得手动添加此文本,您可以编写自己的 JavaDoc 扩展并定义您自己的关键字来为您生成该文本。

或者您创建自己的Annotation 并在主要描述中参考。我下面的示例同时进行(注释和描述性文本),让您了解我在说什么(注释@IsMandatory 的定义被省略)。

但通常,Builder 不会有强制属性的方法;相反,这些是Builder 的构造函数的参数。

/**
 *  <p>{@summary Builder for new instances of
 *  {@link MyObject}.}</p>
 *  <p>Attributes whose setter methods are marked with the
 *  {@link IsMandatory &#64;IsMandatory}
 *  annotation are – obviously – mandatory. If not set before
 *  {@link #build()}
 *  is called, an
 *  {@link IllegalStateException}
 *  will be thrown.</p>
 *  <p>In particular, these are the attributes</p>
 *  <ul>
 *    <li>{@link #setName(String) name}</li>
 *    … 
 *  </ul>
 */  
public final class MyObjectBuilder
{
  /**
   *  Creates a new instance of {@code MyObjectBuilder}.
   */
  public MyObjectBuilder() {…}

  /**
   *  Creates a new instance of
   *  {@link MyObject}.
   *
   *  @return The new instance.
   *  @throws IllegalStateException A mandatory attribute was not yet set.
   */
   public final MyObject build() throws IllegalStateException {…}

  /**
   *  <p>{@summary Sets the name for the new instance of
   *  {@link MyObject}.} It can be any arbitrary string with more than
   *  one character that is not
   *  {@linkplain String#isBlank() blank}.</p>
   *  <p><b>Note:</b> This attribute is mandatory! If missing,
   *  {@link #build()}
   *  will throw an
   *  {@link IllegalStateException}.</p>
   *
   *  @param name The name for the new instance.
   *  @throws NullPointerException {@code name} is {@code null}.
   *  @throws IllegalArgumentException {@code name} is the empty string, or
   *      it is
   *      {@linkplain String#isBlank() blank}.
   */
  @IsMandatory
  public final void setName( final String name ) throws NullPointerException, IllegalArgumentException {…}

  /**
   *  Sets the other attribute for the new instance of
   *  {@link MyObject}.
   *
   *  @param other The other attribute.
   */
  public final void setOther( final Object other ) {…}
}

玩得开心!

【讨论】:

  • 感谢您提供详细且示例支持的解释。这非常有帮助。
猜你喜欢
  • 2014-08-25
  • 2015-11-03
  • 1970-01-01
  • 2011-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-09
  • 2015-08-14
相关资源
最近更新 更多