实用程序类应该是静态的吗?[关闭]


76

如果我必须设计一个Utility类(例如ByteUtils或StreamUtils或StringUtils),那么对他们来说最佳的设计选择是什么。

  • 它们应该是静态类(因为我将没有任何状态可存储)
  • 它们应该是非静态类吗(这样,如果不使用对象,它们将被gc'd)

PS:静态类是指具有静态方法的类(而不是内部静态类)

请为此提供设计选择方面的建议?


5
您需要重新表达这个问题。外部类在Java中不能是静态的。他们只能有静态方法。不一样的东西。
罗恩侯爵,

是。你是对的。我的意思是一个方法是静态的类
nibin012

尽管您的示例(字节,字符串,流)应该很小,但是如果您碰巧在EJB中有一个1k LOC,并且使用了具有静态方法的映射器实用程序,那么对该EJB进行测试就变得非常困难。
LAFK说,请

Answers:


46

如果它是通用工具,则使用IMO更好。您说过将没有任何状态可存储,所以我不明白为什么要使其变为非静态。声明为静态将节省内存。


10
螺丝起子不会改变,它总是做同样的事情。如果可以对您的实用程序类说相同的话,请务必使其变为静态。
加普顿2011年

10
这对于不依赖于对象状态的所有方法都适用。不只是实用程序类方法。
Dorus

71

我的实用程序类如下所示:

// final, because it's not supposed to be subclassed
public final class FooUtil {

    // private constructor to avoid unnecessary instantiation of the class
    private FooUtil() {
    }

    public static int doSomethingUseful() {
    }

    // ...
}

请注意,尽管这使实用程序方法易于测试,并且可以从外部轻松访问,但也使使用它们的类难以进行单元测试,因为模拟这些实用程序方法并不容易。此类实用程序类过多可能表明缺乏OO设计(过程编程),并且确实使代码难以测试。

如果您使用的是依赖项注入框架(Spring,Guice等),最好使非静态方法使实用程序类可实例化,并使其成为可注入的单例。这样,可以通过模拟实用程序对象来测试使用这些实用程序方法的类。


2
我们可以使用powermock code.google.com/p/powermock
nibin012

13
@ nibin012仅仅是因为您可以,并不意味着您应该这样做。在我的项目中,PowerMock是一个持续悲伤的工具-与其他工具一起使用时,它很糟糕,因为它与类加载器的冲突太多了。
LAFK说恢复莫妮卡(Monica)2014年

@ jb-nizet能否请您提供以下示例:>最好只使用非静态方法使实用程序类可实例化,并使其成为可注入的单例。它是SGTM,但我很难实施。
alexsalo '16

1
“如果您使用的是依赖注入框架”-否则,这不是一个好主意吗?
在线

您不是单元测试,只是因为您使用了powermocks。通常,使用诸如powermock之类的模拟来测试您设计的静态方法在某种程度上可能是错误的,因为您将覆盖“无状态”方法。
Wisienkas

24

仅仅因为某些事物可以是静态的,并不意味着它应该是静态的。

所有这一切还有另一个考虑因素:嘲笑。在测试中模拟静态方法要比模拟类实例的行为难。

对我来说,谈论不必要的堆分配和对象的GC可能带来过早的优化。JVM将在解决此类问题方面做得很好。


9

定义Utility类的最简单方法是作为没有实例的枚举

public enum Utility {;
     public static int utilityMethod(int x) { /* ... */ }
}

实用程序类不应具有任何状态或具有最小状态,因此您不必担心GC。

您可以具有用于特定目的的其他状态类,例如Builder,Factory,可以根据需要创建对象,并在完成后将其丢弃。


1
我假设您不是指静态嵌套类。这避免了记住记住创建私有构造函数的情况,以防止人们意外地创建实用程序类的实例。final默认情况下,枚举是。
彼得·劳瑞

1
在Java中,enum可以具有可变字段,实现接口和抽象方法,因此它们不仅仅是常量。;)
Peter Lawrey 2011年

3
@PeterLawrey,虽然这是事实,但对我来说有点工程过度。自1996年以来,我可以算出总和为零次,因为我看到有人不小心实例化了一个旨在成为简单静态实用程序的类。提供这样的保护是愚蠢的IMO,因为实例化此类结果的后果是您立即注意到它将无法正常工作。把在enum混进去然而改变在凌晨3点累工程师如何跳上咖啡因会读的东西,让他纳闷了一下是怎么回事什么。这不是一个常见的成语。使用class并开心。

4
我在这里真正要表达的主要观点是,当您使用时enum,您是在向读者说明,在其设计中隐藏了枚举的原因。没有。您只想使其比放入final和私有构造函数容易,这都不是IMO的问题。那是不寻常的成语。您看到有人试图实例化实用程序的静态方法类吗?这有关系吗?如果有人认为该类提供了所需的方法,他们就会知道它们是什么-为什么要实例化它?如果他们这样做会怎样?不多。

3
我认为我们已经有一段时间以来一直在质疑我们的一些过度保护思想。迷恋上了gotos,避免了多次返回,实例化保护等,实际上,所有这些方面的好处非常有限。然而什么值得保护的是代码的可读性。始终尝试尽可能地接近无脑类别。

4

使用私有构造函数将类设为非静态是很好的:

  • 如果我们将类设为静态,则将在部署应用程序时加载该类;如果它是非静态的,则将在调用其静态方法之一时加载该类。
  • 创建私有构造函数将避免实例化该类

那么该如何使用此类?带有私有结构的
非静态

3

通常,实用程序类包含静态方法而没有属性,这种方法使无需实例化类即可更轻松地使用其方法。希望这可以帮助。


2

如果您可以将它们设为静态,那么一定要这样做!

换句话说,如果它们没有状态,则它们应该是静态的。


1
我已经在遵循该原则的项目中进行了编码。“如果可以的话”。由于这一点,测试是噩梦般的。而PowerMock则使情况变得更糟,这是由于它的类加载器魔术与其他工具之间的冲突从来都不容易理解。不赞成投票。
LAFK说恢复莫妮卡2014年

1
@LIttleAncientForestKami,我总体上同意“如果可以的话”。但是,我认为Petar所说的是“如果没有必要使用除静态以外的任何东西,那么请使用静态”(或更简洁的词)。至少这就是我从他的发言中得出的意思。IOW,“如果您不需要一个实例,那么就不要仅创建一个实例。” 再次,我接受他的发言。

@tgm,是的。谢谢!
Petar Ivanov

2

好吧,如果您没有要存储的状态,那么GC就没有任何内容,因此我将使用static,这样就可以避免任何不必要的堆分配和GC。


2

纯实用程序类通常应该是静态的。当您的类具有明确定义的输入和输出,没有副作用,没有状态时,那么根据定义,它应该是静态类。

通常,不要在必要之前增加复杂性(在这种情况下为依赖注入),这样做会带来好处。


1

这里没有人提到静态实用程序方法是不可扩展的。您无法覆盖它们。您无法利用OOP(尤其是多态性,这是OOP的最强大功能)。这导致代码重复。

PS我发现这篇文章非常有帮助。http://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html


定义“可扩展” ...没有什么可以阻止您在调用现有工具的自己的工具类中进行重载(与IMO的子类化/重载没有太大区别),并且如果要防止代码重复,则可以始终使用泛型以便您的工具功能可以处理更多类型的数据。
Nyerguds

该文章似乎是为了“使用对象,因为它是OOP”的思维方式,而IMO只是内存污染。它不仅给GC带来了额外的工作,而且由于可以存储对象引用,因此所有对象创建都存在创建内存泄漏的风险。不值得。
Nyerguds

不知道为什么不赞成这样做。一篇很好的文章总结了为什么这是更好的选择:vojtechruzicka.com/avoid-utility-classes
Pratik

@Nyerguds对于GC的额外工作,您绝对是错误的。通常的方法是使用私有构造函数创建一个单例(静态getInstance()方法)。
Pratik

@Pratik这是内存污染,因为它会无限期保留这些对象。
Nyerguds
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.