在参数上调用静态方法而不在参数中实例化类

时间:2013-05-06 06:44:26

标签: java

我真的进入了TDD,我开始在jUnit中使用mockito来提高我测试代码的能力。我真的很喜欢模仿!

我注意到我必须改变我对编码的看法,比如尽可能地将协作者传递给方法,并尽可能限制构造函数中的工作。

以下情况需要来自SO专家的一些建议。

假设我有一个方法,那就是在某个类上调用一些静态方法。 E.G。

public void method(){
    OtherClass.staticMethod();
}

这通常很糟糕,但在我的场景中需要它。为了使代码在单元测试中更易于测试,我想避免依赖于OtherClass并将其作为参数传递。

这不起作用,因为它会产生编译时错误。

public void method(Class<? extends OtherClass> util){
    util.staticMethod();
}
...
method(OtherClass.class);

这可行,但我不喜欢实例化的OtherClass,如果我不需要,因为它只是一类静态实用程序,如方法:

public void method(OtherClass util){
     util.staticMethod();
}
...
method(new OtherClass());

我向你提问: 如果不使用新关键字,是否有更好的更好的方法来实现这一目标?

7 个答案:

答案 0 :(得分:1)

  

这可行,但我不喜欢实例化的OtherClass,如果我不需要,因为它只是一类静态实用程序,如方法:

public void method(OtherClass util){
     util.staticMethod();
}
...
method(new OtherClass());

实际上,这不起作用,因为它总是从OtherClass调用方法实现,而不管你传递的对象(即使你传递null)。

我强烈建议不要仅使用反射来简化测试,因为这会绕过编译时检查(编译器不会检测到错误拼写的方法名称),并阻止使用IDE的许多功能(代码完成, javadoc悬停,重构支持,调用层次结构显示,跳转到定义,...)

常见的方法是使用多态分派。在Java中,这要求该方法不是静态的而不是私有的。因此经验法则是:如果需要嘲笑,它不应该是静态的。

如何最好地获取对象实例取决于您的具体情况;依赖注入(您的方法),资源定位器模式和单例模式各有其优缺点。

答案 1 :(得分:0)

在您的代码示例中:

public void method(){
    OtherClass.staticMethod();
}

我的理解是,除了静态方法调用之外,上面的方法应该有一些逻辑,这就是你想要测试而不是静态方法。

如果是这样,你可以编写OtherClass.staticMethod的模拟实现,它将绕过所有逻辑或返回所需的值或实现特定的逻辑。这将避免对OtherClass的依赖,并为测试提供明确的控制。

答案 2 :(得分:0)

您可以通过更改此方法来使用您的第一种方法

public void method(Class<? extends OtherClass> clazz) throws Exception {
    Method[] mArray = clazz.getMethods();
    for(Method m :mArray) {
        if(!m.isAccessible()) m.setAccessible(true);
        if((m.getModifiers() & Modifier.STATIC) != 0) { // Here a method which is static is executed. You can change this condition, to suit your needs
            m.invoke(null);
        }
    }
}

您可以通过调用

来调用它
method(OtherClass.class);

没有传递该类的新对象

答案 3 :(得分:0)

你可以使用反射:

// exceptions management omitted
public void method(Class<? extends OtherClass> clazz, String methodName) {
    // methodName could also be a constant if it won't change
    clazz.getMethod(methodName).invoke(null);
}

然后,

method(OtherClass.class, "staticMethod");

答案 4 :(得分:0)

以下是我刚刚尝试的内容:

public void testMethod(Class<T> clazz) throws Exception{        
        Method m = clazz.getMethod("staticMethod",null);
        m.invoke(null,null);
    }

我假设(如您的示例中所述)静态方法不接受任何参数。

请参阅getMethod()inovke()方法的文档。

答案 5 :(得分:0)

这里和那里的一些静态代码没有错 - 如果它不访问全局状态:

如果静态代码纯粹是程序性的

public void method(){
    OtherClass.staticMethod();
}

只要将静态调用作为方法中唯一的操作

,就可以围绕method()进行模拟。

否则,如果您的静态方法保护某些全局状态,那么使用Singleton + Locator会更好

public class Registry {
    DoSomethingInterface getOtherClass() {
        return OtherClass.getSingleton();
    }
}


public void method(Registry reg){
    reg.getOtherClass().doSomething();
} 

然后,您可以将注册表子类化为getOtherClass()定位器提供所有类型的变体,这样您就可以在测试时将TestRegistry实例化,从而提供干净的全局状态和其他内容

如果您仍然需要其他类是静态的,因为您无法以任何方式更改它(即库类),您可以将其包装在您的界面中:

public class Registry {
    DoSomethingInterface getOtherClass() {
        return new DoSomethingInterface(){
            public void doSomething() {
                OtherClass.staticMethod();
            }
        };
    }
}

答案 6 :(得分:0)

我不确定这是否有帮助。但我不得不做类似的事情,虽然不是为了测试目的。这就是我想出来的。

创建一个类,一旦实例化只有一个方法,它调用另一个类静态方法。如果你想更多地解耦它,那么创建一个接口。类似的东西:

public interface MethodCaller {
    void callMethod();
}

然后使用此实例作为方法的参数。

public void method(MethodCaller caller){
    caller.callMethod();
}

然后打电话给你。

method(new MethodCaller() {
   @Override
   public void callMethod() {
       OtherClass.staticMethod();
   }
});

希望这有帮助!