Java 最佳实践:只有静态方法的类
Posted
技术标签:
【中文标题】Java 最佳实践:只有静态方法的类【英文标题】:Java best practice: Class with only static methods 【发布时间】:2015-10-03 06:55:16 【问题描述】:我有一个应用程序,其中有一个名为PlausibilityChecker
的类。这个类只有静态方法,比如checkZipcodeFormat
或checkMailFormat
。我在我的 GUI 类中使用它们来检查输入,然后再将其发送到较低层。
这是好的做法吗?我以为我只会使用静态方法,这样我就不必关心将实例传递给 GUI 类或在每个不引用 gui 对象的 gui 类中拥有一个实例字段。
我注意到Java NIO
的Files
类只有静态方法,所以我认为这不会是大错特错。
【问题讨论】:
只要没有状态就可以了。 静态类是可以的,只是不要忘记添加一个私有构造函数。啊,注意多线程访问。 提供有关程序结构的更多信息。 我们通常将这样的类命名为*Helper,拥有它们并没有错。额外的private constuctor()
可以防止实例化,
我也经常看到它命名为“Utility”
【参考方案1】:
在 Java 8 中,您现在可以将静态实用程序类更改为具有静态实现的接口。这消除了将类设为 final 并且必须提供私有构造函数的需要。就像将“类”更改为“接口”并删除最终单词(如果有)一样简单(所有接口都是抽象的,因此它们不能是最终的)。由于接口方法始终是公共的,您可以从中删除任何公共范围。如果你有一个私有构造函数,那么也删除它(你不能用构造函数编译接口,因为它们不能被实例化)。它的代码更少,看起来更干净。您不必重构任何已经使用它的类。
【讨论】:
语言级别“7”不支持扩展方法。静态接口方法需要 API 级别 24【参考方案2】:我会说你做得对。除此之外,对您的实用程序类的一些建议:
确保它没有任何状态。也就是说,除非声明为static final
,否则类中没有字段。此外,请确保该字段也是不可变的,例如String
s.
确保它不能是其他类的超类。创建类 final
以便其他程序员无法扩展它。
这个值得商榷,但你可以声明一个无参数构造函数private
,所以没有其他类可以创建你的实用程序类的实例(使用反射或类似的东西可以,但没有必要去保护与班级)。为什么你不能这样做?好吧,这是您想要/需要注入实用程序类的实例的奇怪情况,例如通过一个接口而不是直接在你的类中使用它。 Here's an example of this。这种设计确实很奇怪,但可能会发生(如上面的链接所示),但如果您不会在这种情况下运行,最好的建议是保留构造函数private
。
有很多库提供实用程序类以帮助我们的程序员完成工作。其中最著名的是Apache Common 库集。它是开源的,您可以检查代码以了解他们如何设计这些实用程序类以创建您的。 (免责声明:我不工作或不支持这些库,我是他们的快乐用户)
重要提示:Avoid using a singleton for your utility class.
【讨论】:
如果他使用单例模式并正常创建除getInstance()
之外的所有功能会更好吗?
@BugsHappen don't use singleton, it's evil.
对于第一个要点,我认为确保任何字段都是静态最终字段还不够,因为最终字段可以引用可变的东西;我想说确保没有什么是可变的,并且不依赖任何外部的东西(文件系统、数据库等)。尽管 nio Files 示例与最后一部分背道而驰。【参考方案3】:
不用担心子类化或实例化。 JDK 中的以下实用程序类可以被子类化或实例化,但多年来没有人滥用它们。人们没有那么愚蠢。
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
但是,作为公共 API,您确实希望禁止使用默认构造函数,否则 javadoc 页面上有一个未记录的构造函数,这很尴尬和令人困惑。对于您自己的内部 API,没关系,没人关心。
尽管如此,没有理由禁止子类化。如果有人想继承一个实用程序类,无论出于何种原因,让他吧。当然,私有构造函数会抑制子类化作为副作用。
在java8中,有更多的设计可能性需要考虑-
具有所有静态方法的接口 - 这与具有所有静态方法的类一样好。接口和类都不是为此目的而设计的,所以任何一个都可以。但是,不要期望在接口的子类型中继承这些静态方法——接口静态方法是不可继承的。使用接口的一个优点是我们不需要禁止默认构造函数出现在 javadoc 中。
具有所有默认方法的接口 - 通过继承访问。这很有趣,但总的来说是有问题的(继承仅在非静态上下文中有效)。但在某些 API 设计中它可能是更好的选择,例如 html builder API。
【讨论】:
这违背了 Joshua Bloch 在他的 Effective Java 书中的建议,他建议您只使用接口来定义类型。【参考方案4】:只有静态方法的类是 Java 中实用方法的常见模式。标准库中的示例包括Files、Collections 和Executors。
对于此类实用程序类,最好确保您的类不能被实例化,以明确该类的意图。您可以通过显式声明私有构造函数来做到这一点。有关详细信息,请参阅 Josh Bloch 的“Effective Java”中的 Item 4: Enforce noninstantiability with a private constructor。
【讨论】:
【参考方案5】:单例也可以,具体取决于上下文。
出于测试目的,您可能希望模拟出对单例的引用,以避免在单元测试期间产生副作用。 在这种情况下,使用像 Spring 这样的依赖注入框架来处理实例的创建并使用可以被覆盖的常规方法而不是静态方法可能是有意义的。
如果您仍打算使用静态方法,请确保调用在多线程上下文中使用时是线程安全的。 (例如双重检查锁定)。
【讨论】:
静态方法!=单例。对于只有静态方法的实用程序类,通常根本没有该类的实例。【参考方案6】:如果您只有为其他类提供“工具”的方法,这很好,为什么这不是一个好的做法。
【讨论】:
以上是关于Java 最佳实践:只有静态方法的类的主要内容,如果未能解决你的问题,请参考以下文章