Java 最佳实践:Class 仅使用静态方法

Java best practice: Class with only static methods

我有一个应用程序,其中有一个名为 PlausibilityChecker 的 class。 class 只有静态方法,如 checkZipcodeFormatcheckMailFormat。我在我的 GUI classes 中使用它们来检查输入,然后再将其发送到下层。

这是好的做法吗?我以为我会只使用静态方法,这样我就不必关心将实例传递给 GUI classes 或在每个不引用的 gui class 中都有一个实例字段一个 gui 对象。

我注意到 Java NIOFiles class 只有静态方法,所以我认为这不会是那么可怕的错误。

如果您只有向其他 类 提供 "tools" 的方法,这很好。

我会说你做得对。除此之外,对您的实用程序的一些建议 class:

  • 确保它没有任何状态。也就是说,class 中没有字段,除非它被声明为 static final。另外,请确保此字段也是不可变的,例如Strings.
  • 确保它不能是其他 class 的超级 class。使 class final 这样其他程序员就无法扩展它。
  • 这个值得商榷,但您可以声明一个无参数构造函数 private,因此其他 class 无法创建您的实用程序 class 的实例(使用反射或其他方式类似的会做,但没有必要用 class) 去保护。为什么你不能这样做?好吧,这是一个奇怪的情况,你 want/need 注入一个实用程序的实例 class 例如通过界面而不是直接在 class 中使用它。 。这种设计确实很奇怪,但可能会发生(如上面的 link 所示),但如果您不会 运行 在这种情况下,最好的建议是保留构造函数 private.

有很多库提供实用程序 classes 以帮助我们程序员的工作。其中最著名的是 Apache Common 库集。它是开源的,您可以查看代码以了解他们如何设计这些实用程序 classes 以创建您的实用程序。 (免责声明:我不工作也不支持这些库,我很乐意使用它们)

重要提示:Avoid using a singleton for your utility class.

类 只有静态方法是 Java 实用方法的常见模式。标准库中的示例包括 Files, Collections, and Executors.

对于此类实用程序 classes,最好确保您的 class 无法实例化,以明确 class 的意图。您可以通过显式声明私有构造函数来实现。有关详细信息,请参阅 Josh Bloch 的 "Effective Java" 中的 Item 4: Enforce noninstantiability with a private constructor

单例也可以,看上下文。

出于测试目的,您可能需要模拟对单例的引用以避免在单元测试期间产生副作用。 在这种情况下,使用 Spring 等依赖注入框架来处理实例的创建并使用可以覆盖的常规方法而不是静态方法可能是有意义的。

如果您仍打算使用静态方法,请确保在多线程上下文中使用的调用是线程安全的。 (例如双重检查锁定)。

不用担心子classing 或实例化。 JDK 中的以下实用程序 classes 可以被子classed 或实例化,但这些年来没有人滥用它们。人们没有那么愚蠢。

java.beans.Beans
java.beans.PropertyEditorManager
java.lang.invoke.LambdaMetafactory
java.lang.reflect.Modifier
java.net.URLDecoder                                   ...but not URLEncoder:)
javax.management.DefaultLoaderRepository
javax.management.Query
javax.management.loading.DefaultLoaderRepository
javax.management.relation.RoleStatus
javax.print.ServiceUI
javax.swing.UIManager
javax.swing.plaf.basic.BasicBorders
javax.swing.plaf.basic.BasicGraphicsUtils
javax.swing.plaf.basic.BasicHTML
javax.swing.plaf.basic.BasicIconFactory
javax.swing.plaf.metal.MetalBorders
javax.swing.plaf.metal.MetalIconFactory
javax.swing.text.Utilities
javax.swing.text.html.HTML

但是,作为 public API,您确实希望取消默认构造函数,否则 javadoc 页面上有一个未记录的构造函数,这很尴尬且令人困惑。对于自己的内部 APIs,没关系,没人关心。

虽然没有理由抑制子classing。如果有人想要 subclass 实用程序 class,无论出于何种原因,让他。当然,私有构造函数会抑制 subclassing 作为副作用。


在java8中,可以考虑更多的设计可能性-

具有所有静态方法的接口 - 这与具有所有静态方法的 class 一样好。 interface 和 class 都不是为此目的而设计的,所以任何一个都可以。但是,不要期望在接口的子类型中继承这些静态方法——接口静态方法是不可继承的。使用接口的一个优点是我们不需要禁止默认构造函数出现在 javadoc 上。

具有所有默认方法的接口 - 通过继承访问。这很有趣,但通常存在问题(继承仅适用于非静态上下文)。但在某些 API 设计中它可能是更好的选择,例如 html builder API.

在 Java 8 中,您现在可以将静态实用程序 classes 更改为具有静态实现的接口。这消除了使 class 最终化并必须提供私有构造函数的需要。它就像将 'class' 更改为 'interface' 并删除最后一个词(如果有)一样简单(所有接口都是抽象的,因此它们不可能是最终的)。由于接口方法总是 public 你可以从它们中删除任何 public 范围。如果您有私有构造函数,那么也将其删除(您无法使用构造函数编译接口,因为它们无法实例化)。它的代码更少,看起来更干净。您不必重构任何已经使用它的 classes。