在java中重构Utility类的最佳方法是什么(静态类)

时间:2015-05-28 11:38:33

标签: java oop design-patterns immutability

我正在考虑重构一些实用程序类(静态类)。 静态类很难测试,主要问题是它的制作 我们的代码非常紧密耦合,很多依赖。 用于重构的最佳设计模式是什么? 我想到了一个构建器的不可变对象,但我不确定

将此代码视为1我要重构

public class UtilTest {

    public static boolean  isEligibleItem(Item item){
         if(isCondition1(item)){
             return isCondition2(item);
         }

         return false;
    }

    public static  boolean  isCondition1(Item item){
        //go to service that go to the data base  
        return false;
    }

    public static boolean  isCondition2(Item item){
        //go to service that go to the data base  
        return false;
    }
}

如果我想测试我的isEligibleItem()方法,我需要模拟转到db的2方法。 我不能这样做,因为它们是静态的。我想避免使用Powermock

2 个答案:

答案 0 :(得分:5)

效用类反模式

人们说静态方法难以测试的原因更多的是它如何将不相关的类紧密耦合在一起它们降低凝聚力以及引入我看不见的副作用这三件事比一些单位测试手挥动投诉更重要

测试交互

除了测试static方法本身之外,更多的是测试与其他代码的交互。这就是Java真正需要Functions作为开头的第一类对象的地方。

在大多数情况下,只有static方法的类绝对是代码味道。也有例外,但这种反模式往往会被非面向对象语言的初学者和旧计时器滥用。

规则的例外 - 不可变

例外情况主要是可能被视为缺失的内容,这些内容被标记为final的{​​{1}}类String Immutable

拥有一般Strings方法的static类并不是那么糟糕,因为String是不可变的(没有副作用)而你无法向String添加任何内容所以你没有很多选择。与Integer之类似,Guava具有此命名约定,它适用于这些不可变对象。

副作用

static方法往往会引入很多副作用。以某种不透明的方式处理对象并操纵该对象的事情是糟糕的,更糟糕​​的是,当他们查找其他对象并根据传入的实例操纵它们时,它们会混淆正在发生的事情并紧密耦合低凝聚力。

高凝聚力

紧密凝聚力与Coupling不同,但它同样重要。它们是同一枚硬币的两面,而忽略一面会导致另一枚硬币遭受损失。

这些static方法应该在他们作为参数的类上,它们与这些类紧密耦合。在这种情况下,为什么他们不在Item班级?

只要您添加另一个static方法,即SomeOtherItem,您就可以将不相关的类间接耦合在一起。

最简单的解决方法是在这种情况下将事物移近Item类。

工厂/供应商模式

如果您确实有general或无法添加到类的内容,因为它是final或其他原因,使用接口和Provider模式是您的使用Factory生成Provider实例的最佳方法甚至更好。

然后,您可以使用类似Guice的内容来注入您需要的任何实现,具体取决于它是否是测试。

甚至还有一个混合Utility模式可以从Provider注入实现,这将为您提供static方法的便利性以及不具备它的灵活性和可维护性

答案 1 :(得分:1)

对更易测试的设置的简单转换是:

public class UtilTest {

    private final MyDatabaseService service;

    public UtilTest(MyDatabaseService service) {
       this.service = service;
    }

    public boolean  isEligibleItem(Item item){
         if(isCondition1(item)){
             return isCondition2(item);
         }
         return false;
    }

    public boolean isCondition1(Item item){
        this.service.goToDataBase();
        return false;
    }

    public boolean isCondition2(Item item){
        this.service.goToDataBase2();
        return false;
    }
}

这并没有消除所有问题,但它是一个开始,你可以使用模拟的数据库服务来测试你的类。

如果您想进一步推进,可以声明一个接口,其中包含您希望UtilTest公开的所有方法(您可能也希望重命名该类...),并使{{1}实现它。使用UtilTest的所有代码都应该被重写以使用界面,然后您可以完全直接地模拟UtilTest。这是否值得,很大程度上取决于UtilTest实际上有多复杂。如果它执行的任务相对简单,你可能会认为它比它的价值更麻烦。然而,如果那里有一些繁重的处理,你肯定想让它变得容易模拟。