接口是 Java 8 中实用程序 类 的有效替代品吗?
Are interfaces a valid substitute for utility classes in Java 8?
在过去十年左右的时间里,我一直在为我的 Java 实用程序 classes 使用下面的模式。 class 仅包含静态方法和字段,声明为 final
因此无法扩展,并且具有 private
构造函数因此无法实例化。
public final class SomeUtilityClass {
public static final String SOME_CONSTANT = "Some constant";
private SomeUtilityClass() {}
public static Object someUtilityMethod(Object someParameter) {
/* ... */
return null;
}
}
现在,随着 Java 8 中 static methods in interfaces 的引入,我最近发现自己在使用实用程序界面模式:
public interface SomeUtilityInterface {
String SOME_CONSTANT = "Some constant";
static Object someUtilityMethod(Object someParameter) {
/* ... */
return null;
}
}
这让我摆脱了构造函数,以及接口中隐含的许多关键字(public
、static
、final
)。
这种方法有什么缺点吗?在实用程序界面上使用实用程序 class 有什么好处吗?
我认为它会起作用。我认为变量 SOME_CONSTANT 在您的 SomeUtilityInterface 中默认为 static final,即使您没有明确这么说。因此,它可以作为一个实用程序使用,但是您是否会遇到一些可变性问题,而常规 class 不会有一些可变性问题,并且所有成员变量都必须是最终变量?只要这不是您对默认方法的特定实现的问题,我想不出问题。
你不应该使用界面。
接口不能有私有常量和静态初始值设定项。
public class Utility {
private Utility() {}
public static final Map<String, Integer> MAP_CONSTANT;
static {
Map<String, Integer> map = new HashMap<>();
map.put("zero", 0);
map.put("one", 1);
map.put("three", 3);
MAP_CONSTANT = Collections.unmodifiableMap(map);
}
private static String PRIVATE_CONSTANT = "Hello, ";
public static String hello(String name) {
return PRIVATE_CONSTANT + name;
}
}
只有当您希望有人会实现它时,您才应该使用接口。例如,java.util.stream.Stream
接口有一堆静态方法,这些方法可能位于 Streams
或 StreamUtils
class 之前 Java 8。但是它是有效的接口也具有非静态方法并且可以实现。 java.util.Comparable
是另一个例子:那里的所有静态方法都只支持接口。您不能禁止用户实现您的 public 接口,但对于实用程序 class 您可以禁止他们实例化它。因此,为了代码清晰,我建议不要使用接口,除非它们旨在实现。
关于@saka1029 回答的注释。虽然你不能在同一个接口中定义辅助私有方法和常量,但在像 MyInterfaceHelper
这样的同一个包中创建一个包私有 class 并不是问题,它将具有所有必要的实现 -相关的东西。一般来说,package-private classes 可以很好地向外界隐藏您的实现细节。
根据创造常量接口模式的人的反模式,我会说虽然你不打算让客户端实现接口,但它仍然是可能的,可能更容易,并且 不应该被允许:
APIs should be easy to use and hard to misuse. It should be easy to do simple things; possible to do complex things; and impossible, or at least difficult, to do wrong things.
尽管如下所述,但这确实取决于目标受众
许多易于使用的设计模式受到了很多批评(上下文模式、单例模式、常量接口模式)。哎呀,甚至像得墨忒耳法则这样的设计原则也因为过于冗长而受到批评。
我不想这么说,但这些决定都是基于意见的。尽管上下文模式被视为一种反模式,它在主流框架中很明显,例如 Spring 和 Android SDK。它归结为环境,以及目标受众。
我能找到的主要缺点 被列为 Constant Interface wiki 中 "downsides" 下的第三个清单:
If binary code compatibility is required in future releases, the constants interface must remain forever an interface (it cannot be converted into a class), even though it has not been used as an interface in the conventional sense.
如果您曾经想过 "Hey, this actually isn't a contract and I want to enforce stronger design",您将无法更改它。但正如我所说,这取决于你;也许您将来不会在意更改它。
最重要的是,@TagirValeev 提到的代码清晰度。接口有被实现的意图;如果您不希望有人实施您提供的 API,请不要使其可实施。但我相信这围绕着 "target audience" 声明。不会说谎,我在不那么冗长的基础上支持你,但这取决于我的代码是为谁准备的;不想为可能会被审查的代码使用常量接口。
在过去十年左右的时间里,我一直在为我的 Java 实用程序 classes 使用下面的模式。 class 仅包含静态方法和字段,声明为 final
因此无法扩展,并且具有 private
构造函数因此无法实例化。
public final class SomeUtilityClass {
public static final String SOME_CONSTANT = "Some constant";
private SomeUtilityClass() {}
public static Object someUtilityMethod(Object someParameter) {
/* ... */
return null;
}
}
现在,随着 Java 8 中 static methods in interfaces 的引入,我最近发现自己在使用实用程序界面模式:
public interface SomeUtilityInterface {
String SOME_CONSTANT = "Some constant";
static Object someUtilityMethod(Object someParameter) {
/* ... */
return null;
}
}
这让我摆脱了构造函数,以及接口中隐含的许多关键字(public
、static
、final
)。
这种方法有什么缺点吗?在实用程序界面上使用实用程序 class 有什么好处吗?
我认为它会起作用。我认为变量 SOME_CONSTANT 在您的 SomeUtilityInterface 中默认为 static final,即使您没有明确这么说。因此,它可以作为一个实用程序使用,但是您是否会遇到一些可变性问题,而常规 class 不会有一些可变性问题,并且所有成员变量都必须是最终变量?只要这不是您对默认方法的特定实现的问题,我想不出问题。
你不应该使用界面。 接口不能有私有常量和静态初始值设定项。
public class Utility {
private Utility() {}
public static final Map<String, Integer> MAP_CONSTANT;
static {
Map<String, Integer> map = new HashMap<>();
map.put("zero", 0);
map.put("one", 1);
map.put("three", 3);
MAP_CONSTANT = Collections.unmodifiableMap(map);
}
private static String PRIVATE_CONSTANT = "Hello, ";
public static String hello(String name) {
return PRIVATE_CONSTANT + name;
}
}
只有当您希望有人会实现它时,您才应该使用接口。例如,java.util.stream.Stream
接口有一堆静态方法,这些方法可能位于 Streams
或 StreamUtils
class 之前 Java 8。但是它是有效的接口也具有非静态方法并且可以实现。 java.util.Comparable
是另一个例子:那里的所有静态方法都只支持接口。您不能禁止用户实现您的 public 接口,但对于实用程序 class 您可以禁止他们实例化它。因此,为了代码清晰,我建议不要使用接口,除非它们旨在实现。
关于@saka1029 回答的注释。虽然你不能在同一个接口中定义辅助私有方法和常量,但在像 MyInterfaceHelper
这样的同一个包中创建一个包私有 class 并不是问题,它将具有所有必要的实现 -相关的东西。一般来说,package-private classes 可以很好地向外界隐藏您的实现细节。
根据创造常量接口模式的人的反模式,我会说虽然你不打算让客户端实现接口,但它仍然是可能的,可能更容易,并且 不应该被允许:
APIs should be easy to use and hard to misuse. It should be easy to do simple things; possible to do complex things; and impossible, or at least difficult, to do wrong things.
尽管如下所述,但这确实取决于目标受众
许多易于使用的设计模式受到了很多批评(上下文模式、单例模式、常量接口模式)。哎呀,甚至像得墨忒耳法则这样的设计原则也因为过于冗长而受到批评。
我不想这么说,但这些决定都是基于意见的。尽管上下文模式被视为一种反模式,它在主流框架中很明显,例如 Spring 和 Android SDK。它归结为环境,以及目标受众。
我能找到的主要缺点 被列为 Constant Interface wiki 中 "downsides" 下的第三个清单:
If binary code compatibility is required in future releases, the constants interface must remain forever an interface (it cannot be converted into a class), even though it has not been used as an interface in the conventional sense.
如果您曾经想过 "Hey, this actually isn't a contract and I want to enforce stronger design",您将无法更改它。但正如我所说,这取决于你;也许您将来不会在意更改它。
最重要的是,@TagirValeev 提到的代码清晰度。接口有被实现的意图;如果您不希望有人实施您提供的 API,请不要使其可实施。但我相信这围绕着 "target audience" 声明。不会说谎,我在不那么冗长的基础上支持你,但这取决于我的代码是为谁准备的;不想为可能会被审查的代码使用常量接口。