我经常发现自己将类中的常见行为提取到helper / utility类中,这些类只包含一组静态方法。我经常想知道我是否应该将这些类声明为抽象类,因为我无法真正想到实例化这些类的正当理由?
优点和缺点是宣布这样一个类是抽象的。
public [abstract] class Utilities{
public static String getSomeData(){
return "someData";
}
public static void doSomethingToObject(Object arg0){
}
}
答案 0 :(得分:71)
你可以声明一个什么都不做的私有构造函数。
声明类“abstract”的问题在于abstract关键字通常意味着该类旨在进行子类化和扩展。这绝对不是你想要的。
答案 1 :(得分:18)
不要打扰它们是抽象的,而是包含一个私有的无参数构造函数来防止它们被实例化。
对于那些感兴趣的人来说比较点:在C#中你会声明这个类是静态的,在编译的表单中使它成为抽象的和密封(Java的最终版),而根本没有任何实例构造函数。这也使得声明该类型的参数,变量,数组等成为编译时错误。方便。
答案 2 :(得分:9)
我没有声明实用程序类abstract,我将它们声明为final并使构造函数为private。这样它们就不能被子类化,也无法实例化。
public final class Utility
{
private Utility(){}
public static void doSomethingUseful()
{
...
}
}
答案 3 :(得分:3)
通过将它们声明为抽象,您实际上向其他编码器指示您打算从这些类派生。真的,你是对的,没有太大的区别,但这里的语义更多的是关于其他看你代码的人的解释。
答案 4 :(得分:3)
我会在私有构造函数之外添加更多步骤:
public class Foo {
// non-instantiable class
private Foo() { throw new AssertionError(); }
}
抛出AssertionError
会阻止同一个类中的方法实例化类(好吧,他们可以尝试)。这通常不是问题,但在团队环境中,你永远不会知道某人会做什么。
关于“abstract”关键字,我注意到在很多实例中子类化的实用程序类:
public class CoreUtils { ... }
public class WebUtils extends CoreUtils { ... }
public class Foo { ... WebUtils.someMethodInCoreUtils() ... }
我相信这样做是为了让人们不必记住要包含哪个实用程序类。这有什么缺点吗?这是一种反模式吗?
此致 LES
答案 5 :(得分:2)
正如其他人所说,建立一个私有的无参数构造函数。除了类本身之外,没有人可以创建它的实例。
正如其他人已经展示了如何使用其他语言,以下是如何在下一个C ++版本中执行此操作,如何使类不可实例化:
struct Utility {
static void doSomething() { /* ... */ }
Utility() = delete;
};
答案 6 :(得分:1)
我认为最好使用私有的无参数构造函数将实用程序类声明为final。此外,此类的所有成员都应该是静态的。
在一个语句中完成所有这些操作的一种简单方法是使用@UtilityClass
annotation of Lombok:
@UtilityClass
public class Utilities{
public String getSomeData() {
return "someData";
}
public void doSomethingToObject(Object arg0) {
}
}
如果您使用@UtilityClass
注释,则可以跳过上面的示例中的静态关键字,因为Lombok在编译过程中会自动添加它们。
答案 7 :(得分:0)
不,但是如果你的语言支持它,那就有一个强有力的论据,即在大多数情况下它们应该(可以)被声明为'静态'...静态告诉编译器它们不能被实例化,并且所有它们中的方法必须是静态的。
Abstract是针对具有基于实例的实现细节的类,将由派生类的实例使用...
答案 8 :(得分:0)
Helper / Utility方法很好。不要担心将它们添加到应用程序或框架内的库中。我见过的大多数框架都使用它们。
话虽如此,如果你想对它们变得非常狡猾,你应该在C#3.0中查看extension methods。使用扩展方法将使您的Utilities更多地成为框架的“整体”部分,这似乎是您尝试通过考虑使它们变得抽象而做的。更不用说扩展方法了很多乐趣!
答案 9 :(得分:0)
import static Utilities.getSomeData;
public class Consumer {
public void doSomething(){
String data = getSomeData();
}
}
答案 10 :(得分:0)
我可以提供一些建设性的建议吗?
如果你做了很多这样的事情,你会遇到两个问题。
首先,采用参数的静态方法通常应该是该参数的对象的一部分。我意识到这对像String这样的对象没有帮助,但是如果它需要你定义的对象,你几乎可以肯定通过将你的帮助器作为该对象的方法来改进对象。
如果它获取所有原生值,您可能可以定义一个它是方法的对象。查看是否可以找到这些本机值的任何分组并将它们分组为对象。如果你只是尝试一下,你会发现这个小迷你物体的许多其他用途,在你知道之前它会非常有用。
另一件事,如果你有一个带有一堆半相关静态方法和静态变量的实用程序类,你几乎总是希望它是一个单例。我通过反复试验找到了这个,但是当你发现你需要超过1(最终你会)时,将单例变成多个(?)然后尝试将静态类更改为多个(更容易)好的,所以我现在正在说话。)
祝你好运。这个东西对我来说大部分都是反复试验的 - 虽然它已经像5年前一样出现了,而且我从来没有找到一个让我后悔没有静态类/方法的实例。