在Java中创建实用程序(不包含任何状态)类的最佳实践是什么。
在大多数情况下,我们最终会为此类任务创建静态方法。 其他可能的方法可能是“创建单例对象”以执行此操作。
当需求是代码应该是容易单元测试时,设计考虑应该是什么?
答案 0 :(得分:25)
如果你稍稍放纵一下我的比喻......
您之前可能已经看过其中一个:
请注意,我们称之为烤面包机。我们不称之为“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 Lang,Strings in Guava甚至Arrays in JDK。
此类也应该是最终的,它应该有一个私有构造函数,以避免继承它或实例化它。
要么使用静态方法,要么使用单例,它应该与单元测试相同。在后一种情况下,您可以编写更多代码(字符)。
我知道OO纯粹主义者会争论这些类的存在,我会倾向于同意它们,但这些只是为了简单起见而添加,你应该限制这些类的数量。
答案 2 :(得分:2)
如果使用Spring之类的框架,则可以使用@Service
注释创建实用程序类。
这将确保它的单一实例(SIngleton),并且是一种简单的方法,可以将其注入任何其他需要其方法的类中。
在任何其他情况下,我建议使用工厂模式或单独使用静态方法将其设为单例。
答案 3 :(得分:0)
静态是一个单身人士。只有在需要使用具有不同属性(设置)值的多个Utility类变体时,才需要使用非静态方法的单例实例。例如,当您的实用程序具有某些属性(即customProperty)并且您需要同时使用两种不同的情况时。
当Utility需要使用customProperty = value1
当Utitlity需要使用customProperty = value2 ...
但它很奇怪,笨拙而且不好......实用程序调用者可以为静态方法提供所需的属性值。 所以,不要坚持这一点。使效用方法始终是静态的,不关心“理论”模式......:)
答案 4 :(得分:0)
在Java中创建实用程序(不包含任何状态)类的最佳实践是什么。
在我看来,最好的方法是尽可能省略实用程序类。
实用程序类颠倒了面向对象编程的思想。当你需要一种新方法时,通常会将它添加到具有最高内聚力的类中。这意味着该类包含该方法所需的大部分信息。其他信息将作为参数传递给方法。如果参数列表太长,通常会指示错误的方法。
在极少数情况下,您确实需要一个实用程序类来为某种类型提供方法。例如。
在大多数情况下,我们最终会为此类任务创建静态方法。其他可能的方法可能是“创建单例对象”以执行此操作。
当需求是代码应该是容易单元测试时,设计考虑应该是什么?
如果从单元可测试性角度看一下实用程序类,并且希望能够模拟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来获取新代码,你应该重新考虑你的设计。