为什么在Java中使用静态辅助方法不好?

时间:2013-01-17 20:16:22

标签: java android static-methods mockito helpermethods

我问,因为我正在尝试使用一个不允许你模拟静态方法的模拟框架(Mockito)。调查一下我发现有不少博客文章说你应该尽可能少的静态方法,但是我很难理解为什么。特别是为什么不修改全局状态的方法基本上是辅助方法。例如,我有一个名为ApiCaller的类,它有几个静态方法。静态方法的一个目的是执行HTTP调用,处理我们的服务器可能返回的任何自定义问题(例如用户未登录)并返回响应。为了简化,例如:

public class ApiCaller {
...
   public static String makeHttpCall(Url url) {
        // Performs logic to retrieve response and deal with custom server errors
        ...
        return response;
   }
}

要使用这一切,我所要做的就是致电ApiCaller.makeHttpCall(url) 现在,我可以轻松地将其设为非静态方法,如:

public class ApiCaller {
...
   public String makeHttpCall(Url url) {
        // Performs logic to retrieve response and deal with custom server errors
        ...
        return response;
   }
}

然后使用此方法调用new ApiCaller().makeHttpCall(),但这似乎是额外的开销。任何人都可以解释为什么这是坏的,如果有一个更好的解决方案,使方法非静态(除了删除关键字),以便我可以使用模拟框架存根这些方法?

谢谢!

5 个答案:

答案 0 :(得分:9)

静态方法的问题是,当它们与您尝试测试的系统无关时,它们很难伪造。想象一下这段代码:

public void systemUnderTest() {
    Log.connectToDatabaseForAuditing();
    doLogicYouWantToTest();
}

connectToDatabaseForAuditing()方法是静态的。您不关心此方法对您要编写的测试的作用。但是,要测试此代码,您需要一个可用的数据库。

如果它不是静态的,代码将如下所示:

private Logger log; //instantiate in a setter AKA dependency injection/inversion of control

public void systemUnderTest() {
    log.connectToDatabaseForAuditing();
    doLogicYouWantToTest();
}

现在,如果没有数据库,您的测试将是微不足道的:

@Before
public void setUp() {
    YourClass yourClass = new YourClass();
    yourClass.setLog(new NoOpLogger());

}

//.. your tests

想象一下,当方法是静态的时,尝试这样做。除了修改记录器以使一个名为inTestMode的静态变量在setUp()中设置为true以确保它不连接到数据库之外,我无法想到一种方法。 / p>

答案 1 :(得分:2)

模块化程度较低。相反,您应该使用实例方法ApiCaller定义接口makeHttpCall(),以便将来可以定义单独的实现。

至少,您将始终拥有2个接口实现,原始版本和模拟版本。

(注意:有一些模拟框架允许你模拟静态方法)

作为附录,虽然在您的具体应用中可能并非如此,但通常使用静态方法表明设计监督较大。设计模块化和可重用性应该在整个应用程序中普遍存在,因为即使你现在不需要 你将来可能也需要它,而且它更难以实现事后改变事情要花费更多的时间。

答案 2 :(得分:1)

PRIVATE静态辅助方法并不坏,事实上,它们在我工作的大公司中实际上是首选。我一直使用Mockito,从调用静态辅助方法的方法访问。

编译器处理静态辅助方法的方式略有不同。创建的字节代码将生成invokestatic指令,如果删除静态,则结果将是其他指令之一,如invokespecial。 invokestatic加载类来访问方法的区别在于,invokespecial首先将对象弹出堆栈。因此可能会有轻微的性能优势(可能不是)。

答案 3 :(得分:0)

当你需要几乎回答你自己的问题时,你不能轻易地嘲笑它们。

特别是当它显示的内容时:进行HTTP调用非常昂贵,并且为 unit 测试代码执行此操作是没有意义的 - 除了集成测试之外。

单元测试需要来自HTTP调用的已知响应(和响应代码),如果您使用不受控制的网络呼叫某人 else的服务,则无法执行此操作。< / p>

答案 4 :(得分:0)

在我看来,这种解释非常简单明了,而且易于理解。

将实用程序方法声明为static,通常会使您的代码更易于理解,这是一件好事。

但是,这种方法有一个严重的局限性:这类方法/类不容易被模拟

因此,如果一个辅助方法具有任何外部依赖项(例如DB),这使得它(因此它的调用方)很难进行单元测试,则最好将其声明为非静态< / strong>。

这允许依赖项注入,从而使方法的调用者更易于进行单元测试。

来源:https://softwareengineering.stackexchange.com/a/111940/204271