使不依赖实例字段的方法变为静态吗?


19

最近,我开始在Groovy中为Java项目的集成测试框架进行编程。我将Intellij IDEA与Groovy插件一起使用,我很惊讶地看到所有非静态方法并且不依赖于任何实例字段,这是一个警告。但是,在Java中,这不是问题(至少从IDE的角度来看)。

是否将所有不依赖于任何实例字段的方法都转换为静态函数?如果为true,这是特定于Groovy还是通常可用于OOP?又为什么呢


这些方法是否调用其他实例方法?还是它们实际上与该类的对象无关?你能给我举个例子吗?
Ray Toal 2013年

Answers:


26

请注意,IDEA也对此Java进行了检查,这被称为Method可能是'static'

此检查报告可以安全地设置为静态的任何方法。如果某个方法未引用其类的任何非静态方法和非静态字段,并且未在子类中覆盖,则该方法可能是静态的。

尽管对于Java代码来说,默认情况下是关闭此检查的(程序员可以自行决定将其打开)。其原因很可能是基于几个相当权威的资料来质疑这种检查的有效性/有用性。

首先,正式的Java教程对方法应为静态的时间有严格的限制:

静态方法的常见用法是访问静态字段。

综上所述,有人可能会争辩说,默认情况下打开提到的检查不符合Java中对静态修饰符的建议使用。

此外,还有其他一些消息来源建议采用明智的方法来使用这种检查背后的想法,甚至不鼓励这样做。

例如,参见Java World文章-Happy Object先生教授静态方法

任何与实例状态无关的方法都可以被声明为静态方法。

请注意,我说“被声明为静态的候选者”。即使在前面的示例中,也没有什么迫使您声明instances()为静态的。将其声明为静态只会使调用更加方便,因为您不需要实例来调用该方法。有时,您将拥有似乎不依赖实例状态的方法。您可能不想使这些方法静态化。实际上,如果您需要在没有实例的情况下访问它们,则可能只想将它们声明为静态。

而且,即使可以将这种方法声明为静态方法,也可能由于其插入设计中的继承问题而不想这样做。看看“有效的面向对象设计”,您将看到一些问题。

Google测试博客上的一篇文章甚至宣称静态方法是可测试性的死亡

让我们进行精神锻炼。假设您的应用程序只有静态方法。(是的,可以编写这样的代码,称为过程编程。)现在想象该应用程序的调用图。如果尝试执行叶子方法,则设置其状态并断言所有极端情况都没有问题。原因是叶子方法不再进行任何调用。随着您离叶子越来越远,更接近于根main()方法,在测试中建立状态的难度将越来越大,而声明事物的难度也将越来越大。许多事情将变得无法断言。您的测试将逐渐变大。一旦到达main()方法,您不再需要进行单元测试(因为您的单元是整个应用程序),您现在有了场景测试。假设您要测试的应用程序是文字处理器。您可以从main方法中断言很多...

有时静态方法是其他对象的工厂。这进一步加剧了测试问题。在测试中,我们依靠这样的事实,即我们可以用不同的方式连接对象,并用模拟替换重要的依赖关系。一旦new经营者被称为我们不能覆盖使用一个子类中的方法。这种静态工厂的调用者永久绑定到静态工厂方法生成的具体类。换句话说,静态方法的损害远远超出静态方法本身。将对象图接线和构造代码对接成静态方法特别糟糕,因为对象图接线是我们隔离测试对象的方式。


您会发现,上面给出的Java默认情况下被关闭检查是很自然的。

IDE开发者将有一个真的很难解释为什么他们认为作为默认设置它,对广泛认可的建议和最佳做法,它是如此重要。

对于Groovy而言,情况大不相同。上面列出的论点均不适用,尤其是关于可测试性的论点,如Javalobby 在Groovy模拟静态方法一文中所述:

如果您要测试的Groovy类在另一个Groovy类上调用了静态方法,则可以使用ExpandoMetaClass,它允许您动态添加方法,构造函数,属性和静态方法...

这种差异可能是为什么Groovy中提到的检查的默认设置相反的原因。在Java中,默认情况下“ on”会引起用户混乱,而在Groovy中,相反的设置可能会使IDE用户感到困惑。

“嘿,该方法不使用实例字段,为什么不警告我呢?” 对于Java(如上所述),这个问题很容易回答,但是对于Groovy而言,没有令人信服的解释。


顺便说一下,R​​eSharper(由JetBrains for C#/ Visual Studio制造)提出了同样的建议
Konrad Morawski 2014年

6
有和没有副作用的静态方法之间存在重要区别。Google摘录确实针对前者。
assylias 2014年

@assylias确实如此,但是它还讨论了工厂方法如何使测试更加困难(因为它紧密耦合了应用程序中的依赖)。使用依赖项注入框架是解决此问题的最佳方法之一,它也解决了许多其他问题。
Per Lundberg

5

很难想象这会对性能产生任何明显的影响。我能看到的唯一好处static是它提供了实例状态不受影响的视觉指示。换句话说,它告诉正在阅读源代码的人某些东西……仅仅因为它存在而对该方法“很感兴趣”。真正的问题是,它澄清还是造成混淆? “为什么该方法是静态的?它是工厂方法吗?不需要对象实例就可以调用它吗?”

另外,有些人不喜欢静态方法,因为他们声称您无法模拟它们,因此它们不可测试,或者类似的东西。当然,如果您出于任何通常的原因将其设置为静态,例如提供工厂方法,那么我全力以赴。我只是不太确定使方法成为静态方法(仅因为它们不接触实例状态)是强制性的。


5
请考虑(在Java中)静态方法不能被覆盖。尽管这不是经常出现的问题,但这意味着以后某些子类无法更改该实现。这并不是说静态方法不是正确的答案,而是设计考虑因素的一部分。

2
@ user40980,这确实是值得考虑的,但也值得考虑的是,继承可能是一种笨拙的工具,经常被滥用,并且一些著名人物认为,所有类都应该final或专门考虑继承来设计。
萨拉

3

我认为这是为了使可能的重构被发现

一旦方法已经取得了一成不变的,它是明确的,它可以在类的被搬了出来,说给一个统一的类。

将其转换为参数之一的实例方法也很容易,通常这就是代码应该在的地方。


-1

请注意,您可能具有实现某些接口的无状态类(例如java.lang.Runnable-您无法使其方法静态化。某些模式(例如Strategy)也可能产生甚至具有多个方法的无状态对象。

静力学是单例的第一个表亲。稍后很难真正地从静态转换为非静态-因此,当您需要添加状态时,您很容易开始引入静态变量。


这如何回答所提问题?
蚊蚋

1
为什么很难从无状态过渡到某些状态?我会说相反。污染纯净的东西比净化被污染的东西容易。
sara
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.