接口是 Java 8 中实用程序类的有效替代品吗?

2022-09-01 17:47:26

在过去的十年左右的时间里,我一直在我的Java实用程序类中使用下面的模式。该类仅包含静态方法和字段,声明为无法扩展,并具有构造函数,因此无法实例化。finalprivate

public final class SomeUtilityClass {
    public static final String SOME_CONSTANT = "Some constant";

    private SomeUtilityClass() {}

    public static Object someUtilityMethod(Object someParameter) {
        /* ... */

        return null;
    }
}

现在,随着Java 8中接口中静态方法的引入,我最近发现自己使用了一种实用程序接口模式:

public interface SomeUtilityInterface {
    String SOME_CONSTANT = "Some constant";

    static Object someUtilityMethod(Object someParameter) {
        /* ... */

        return null;
    }
}

这使我摆脱了构造函数,以及接口中隐含的许多关键字(,,)。publicstaticfinal

这种方法有什么缺点吗?在实用程序接口上使用实用程序类有什么好处吗?


答案 1

只有当您期望有人会实现接口时,才应使用接口。例如,接口有一堆静态方法,这些方法可能位于Java 8之前的某个类中。但是,它是一个有效的接口,它也具有非静态方法并且可以实现。这是另一个例子:那里的所有静态方法都只支持接口。您不能禁止用户实现您的公共接口,但对于实用程序类,您可以禁止他们实例化它。因此,为了代码的清晰性,我建议不要使用接口,除非它们打算实现。java.util.stream.StreamStreamsStreamUtilsjava.util.Comparable

关于@saka1029答案的说明。虽然您确实无法在同一接口中定义帮助器私有方法和常量,但在同一包中创建一个包私有类并不是问题,例如它将具有所有必要的实现相关内容。一般来说,包私有类可以很好地向外部世界隐藏您的实现细节。MyInterfaceHelper


答案 2

基于创建Constant Interface模式的人的反模式,我想说,尽管你不打算让客户端实现接口,但它仍然是可能的,可能更容易,不应该被允许

API 应该易于使用且不易误用。做简单的事情应该很容易;可以做复杂的事情;不可能,或者至少很难做错事。

尽管如下所述,但这实际上取决于目标受众


许多易于使用的设计模式受到了很多批评(上下文模式,单例模式,常量接口模式)。哎呀,即使是像德米特定律这样的设计原则也会因为过于冗长而受到批评。

我不想这么说,但这些决定是基于意见的。虽然上下文模式被视为反模式,但它在Spring和Android SDK等主流框架中很明显。它归结为环境以及目标受众。

我能找到的主要缺点是列为Constant Interface wiki中“缺点”下的第三个列表:

如果将来的版本需要二进制代码兼容性,则常量接口必须永远保持为接口(它不能转换为类),即使它尚未用作传统意义上的接口。

如果你曾经想过“嘿,这实际上不是一个合同,我想强制执行更强大的设计”,你将无法改变它。但正如我所说,这取决于你;也许你将来不会在乎改变它。

最重要的是,@TagirValeev提到的代码清晰度。接口具有实现的意图;如果您不希望有人实现您提供的API,请不要使其可实现。但我相信这围绕着“目标受众”的说法。不打算撒谎,我和你在不那么冗长的基础上,但这取决于我的代码是为谁准备的;不想对可能被审查的代码使用常量接口。