什么是在Java中创建无状态实用程序类的最佳实践

时间:2016-12-06 13:31:43

标签: java design-patterns

在Java中创建实用程序(不包含任何状态)类的最佳实践是什么。

在大多数情况下,我们最终会为此类任务创建静态方法。 其他可能的方法可能是“创建单例对象”以执行此操作。

当需求是代码应该是容易单元测试时,设计考虑应该是什么?

5 个答案:

答案 0 :(得分:25)

如果你稍稍放纵一下我的比喻......

您之前可能已经看过其中一个:

Device

请注意,我们称之为烤面包机。我们称之为“BreadUtil。”

同样,实用程序方法可以而且应该放在以特定功能命名的类中,而不是“与面包相关的各种东西。”

大多数情况下,您的静态方法属于相关类;例如,Integer.parseInt是Integer类的静态方法,不是理论上的IntegerUtil或NumberUtil类的成员。

过去,创建单独的实用程序类的一种情况是当感兴趣的主要类是接口时。一个例子是java.util.Collections。但是,从Java 8开始,这不是一个借口,因为接口可以有静态方法和默认方法。实际上,Collections.sort(List)已经迁移到List.sort

如果你有很多实用方法,并且你觉得它们会使相关的类混乱,可以将它们放在一个单独的类中,但不能放在“BreadUtil”类中。将“util”放在类名(或“utils”,“utilities”,“misc”,“miscellaneous”,“general”,“shared”,“common”或“framework”)中是绝对不可接受的。 。为类提供一个有意义的名称,用于描述方法的用途。如果方法太多而不允许这样的类名,则可能需要将它们拆分为多个类。 (只有少数方法的小班是完全可以接受的;许多人甚至认为这是一个好的设计。)

回到Integer示例,如果您觉得这些方法使类混乱,您可以创建这样的新类:

host

最后一个是没有静态方法可能更好的例子:

public class IntegerMath {
    private IntegerMath() { }

    public static int compare(int x, int y) { /* ... */ }
    public static int compareUnsigned(int x, int y) { /* ... */ }
    public static int divideUnsigned(int dividend, int divisor) { /* ... */ }
    public static int min(int a, int b) { /* ... */ }
    public static int max(int a, int b) { /* ... */ }
    public static int remainderUnsigned(int dividend, int divisor) { /* ... */ }
    public static int signum(int i) { /* ... */ }
    public static int sum(int a, int b) { /* ... */ }
    public static long toUnsignedLong(int i) { /* ... */ }
}

public class IntegerBits {
    private IntegerBits() { }

    public static int bitCount(int i) { /* ... */ }
    public static int highestOneBit(int i) { /* ... */ }
    public static int lowestOneBit(int i) { /* ... */ }
    public static int numberOfLeadingZeros(int i) { /* ... */ }
    public static int numberOfTrailingZeros(int i) { /* ... */ }
    public static int reverse(int i) { /* ... */ }
    public static int reverseBytes(int i) { /* ... */ }
    public static int rotateLeft(int i, int distance) { /* ... */ }
    public static int rotateRight(int i, int distance) { /* ... */ }
}

public class IntegerParser {
    private IntegerParser() { }

    public static int parseInt(String s) { /* ... */ }
    public static int parseInt(String s, int radix) { /* ... */ }
    public static int parseUnsignedInt(String s) { /* ... */ }
    public static int parseUnsignedInt(String s, int radix) { /* ... */ }
}

答案 1 :(得分:5)

我认为最常见的方法是创建静态方法。例如,请参阅StringUtils in Apache Commons LangStrings in Guava甚至Arrays in JDK

此类也应该是最终的,它应该有一个私有构造函数,以避免继承它或实例化它。

要么使用静态方法,要么使用单例,它应该与单元测试相同。在后一种情况下,您可以编写更多代码(字符)。

我知道OO纯粹主义者会争论这些类的存在,我会倾向于同意它们,但这些只是为了简单起见而添加,你应该限制这些类的数量。

答案 2 :(得分:2)

如果使用Spring之类的框架,则可以使用@Service注释创建实用程序类。 这将确保它的单一实例(SIngleton),并且是一种简单的方法,可以将其注入任何其他需要其方法的类中。

在任何其他情况下,我建议使用工厂模式或单独使用静态方法将其设为单例。

答案 3 :(得分:0)

静态是一个单身人士。只有在需要使用具有不同属性(设置)值的多个Utility类变体时,才需要使用非静态方法的单例实例。例如,当您的实用程序具有某些属性(即customProperty)并且您需要同时使用两种不同的情况时。

  1. 当Utility需要使用customProperty = value1

  2. 当Utitlity需要使用customProperty = value2 ...

  3. 但它很奇怪,笨拙而且不好......实用程序调用者可以为静态方法提供所需的属性值。 所以,不要坚持这一点。使效用方法始终是静态的,不关心“理论”模式......:)

答案 4 :(得分:0)

  

在Java中创建实用程序(不包含任何状态)类的最佳实践是什么。

在我看来,最好的方法是尽可能省略实用程序类。

实用程序类颠倒了面向对象编程的思想。当你需要一种新方法时,通常会将它添加到具有最高内聚力的类中。这意味着该类包含该方法所需的大部分信息。其他信息将作为参数传递给方法。如果参数列表太长,通常会指示错误的方法。

在极少数情况下,您确实需要一个实用程序类来为某种类型提供方法。例如。

  • 如果你不能将方法添加到源代码中,因为你没有它(但你可以创建一个包装器或子类)
  • 如果需要附加方法的类是final并且您不拥有源代码(例如StringUtility)
  

在大多数情况下,我们最终会为此类任务创建静态方法。其他可能的方法可能是“创建单例对象”以执行此操作。

     

当需求是代码应该是容易单元测试时,设计考虑应该是什么?

如果从单元可测试性角度看一下实用程序类,并且希望能够模拟utiltiy类,则应该使用单例,因为可以替换对象引用。

通过使用对象字段更改单例实例引用(静态变量)或在客户端更改。 E.g。

 private SomeUtility someUtility = SomeUtiltiy.INSTANCE;

 public void someMethod(...){
    // Can be replaced by changing the someUtility reference
    someUtility.doSomething(....); 

    // A static call like 
    // SomeUtility.doSomething(...);
    // can not be easily replaced.
 }

静态方法调用很难替换。一些测试框架如powermock通过重写客户端字节码来支持mocking of static calls。但我认为这些框架旨在支持对遗留代码进行单元测试。如果你需要使用powermock来获取新代码,你应该重新考虑你的设计。