Answers:
将静态成员放入接口(并实现该接口)是一种不好的做法,甚至还有一个名称,即Constant Interface Antipattern,请参见Effective Java,第17项:
恒定接口模式是对接口的不良使用。类内部使用一些常量是一个实现细节。实现常量接口会导致此实现细节泄漏到类的导出API中。对类的用户而言,该类实现一个常量接口并不重要。实际上,它甚至可能使他们感到困惑。更糟糕的是,它表示一种承诺:如果在将来的版本中对该类进行了修改,使其不再需要使用常量,则它仍必须实现该接口以确保二进制兼容性。如果非最终类实现了常量接口,则其所有子类的名称空间都将受到接口中常量的污染。
Java平台库中有几个常量接口,例如
java.io.ObjectStreamConstants
。这些接口应被视为异常并且不应被仿真。
为了避免常量接口的某些缺陷(因为您不能阻止人们实现它),应该首选带有私有构造函数的适当类(例如,从Wikipedia借用的示例):
public final class Constants {
private Constants() {
// restrict instantiation
}
public static final double PI = 3.14159;
public static final double PLANCK_CONSTANT = 6.62606896e-34;
}
要访问常量而不必完全限定常量(即不必在类名前添加常量),请使用静态导入(自Java 5起):
import static Constants.PLANCK_CONSTANT;
import static Constants.PI;
public class Calculations {
public double getReducedPlanckConstant() {
return PLANCK_CONSTANT / (2 * PI);
}
}
“ 恒定的接口模式是对接口的不良使用 ”
不管这个假说是谁,无论他/她是谁,都可以基于有效地继续养成不良习惯和习惯的需要来假想。该假设是基于对不良软件设计习惯有效性的促进。
我在此处针对该假设写了一个反驳:在Java中实现常量的最佳方法是什么?解释这个假设的无根据。
十年来,这个问题一直悬而未决,直到我发表我的理由使这一假设变得合理之后的两个小时之内,这个问题才得以解决,从而使那些坚守这一被错误引导的假设的人们深深地感到不知所措。
这些是我针对假设表达的观点
保持该假设的基础是需要方法和限制性规则来应对不良软件习惯和方法论的影响。
支持“ 不变的接口模式是对接口的不良使用 ” 这一观点的拥护者,除了由于需要应付那些不良习惯和习惯所造成的原因外,无法提供任何其他原因。
解决根本问题。
然后,为什么不充分利用Java开发语言结构的每种语言功能,以方便您使用呢?无需夹克。为什么要制定规则来限制您无效的生活方式,以区别和赋予更有效的生活方式?
是信息组织。在对流程进行工程设计或补充解决方案之前,应首先了解与流程相关的信息,并首先了解该信息的行为以及所谓的业务规则。这种信息组织方法在几十年前被称为数据标准化。
然后,仅解决方案的工程是可能的,因为将解决方案的组件的粒度和模块化与信息组件的粒度和模块化对齐是最佳策略。
组织信息有两个或三个主要障碍。
缺乏对数据模型“规范化”需求的认识。
EF Codd关于数据标准化的陈述是有缺陷的,有缺陷的和模棱两可的。
伪装成敏捷工程的最新时尚是一种误导性的观念,即人们不应该计划和限制未来的模块组织,因为您可以随心所欲地进行重构。重构和持续变化不受将来发现的阻碍被用作借口。然后,通过使用会计技巧来延迟利润和资产配置,来实质性发现过程信息的行为,因此,现在认为不需要基本知识及其处理方法。
不要仅仅因为您喜欢临时的即插即用编程习惯,就制定规则或对它发布任何废话。
不要因为某些人不知道如何操作枪支或容易滥用枪支而禁止枪支拥有。
如果您炮制的规则是为无法进行专业编程的新手编写的,并且您认为自己属于其中,那么请这样说-请勿声明您的Fatwa适用于正确归一化的数据模型。
我不在乎开国元勋对美国宪法的初衷。我不在乎那些未成文的未成文的意图。我只关心书面《宪法》中的文字内容,以及如何利用它们来实现社会的有效运转。
我只在乎Java语言/平台规范允许我做什么,我打算充分利用它们来为我提供一种有效且高效地表达我的软件解决方案的媒介。无需夹克。
它需要编写额外的代码以将参数映射到值。Java的创建者没有提供参数值映射的事实,而无需您编写映射代码就证明了Enum常量就是对Java语言的意外使用。
尤其是由于不鼓励您对参数进行规范化和组件化,因此会产生错误的印象,即混合到Enum包中的参数属于同一维。
别忘了 如果您设计并规范化了数据模型,并且它们包含常量,那么这些常量就是合同。如果您没有规范化数据模型,那么您应该遵守关于如何练习限制性编码以应对这种不良习惯的说法。
因此,接口是实现Constants合同的理想方式。
对。任何人都可能会无意间实现任何接口。没有什么可以阻止这种无心的程序员。
不要放置限制性命令来保护假定的不良做法,这些不良做法会导致未约定/杂散参数泄漏到API中。解决根本问题,而不是怪罪于接口常数。
没有一个正常运行且有效的程序员来证明她可以在水下呆多长时间,在炎热或潮湿的雷暴天气中可以走多远。她将使用汽车,公共汽车或至少一辆自行车等高效工具,每天花10英里去上班。
不要仅仅因为对IDE少的编程有一种深奥的禁欲主义而对其他程序员没有限制。
OSGI是这样的框架。反对接口常量的法令也是如此。
接口常量是将Contract精心设计和规范化的数据模型组件放入Contract的一种有效方式。
嵌套在类文件中的适当命名的私有接口中的接口常量也是对所有私有常量进行分组而不是将它们分散在整个文件中的一种好习惯。
我现在几次遇到这个老问题,但被接受的答案仍然使我感到困惑。经过多番思考,我认为这个问题可以进一步澄清。
只需比较一下:
public final class Constants {
private Constants() {
// restrict instantiation
}
public static final double PI = 3.14159;
public static final double PLANCK_CONSTANT = 6.62606896e-34;
}
与
public interface Constants {
double PI = 3.14159;
double PLANCK_CONSTANT = 6.62606896e-34;
}
用法相同。更少的代码。
我认为@Pascal Thivent的答案强调的错误,这是我的说法:
将静态成员放入接口(并实现该接口)是一种不好的做法。
有效Java中的引言假设常量接口由其他人实现,我认为这不会(也不会)发生。
当您创建名为的常量接口时Constants
,任何人都不应实现它。(尽管在技术上可行,但这是这里唯一的问题)
标准库不能承担任何可能的滥用设计,因此您不会在其中看到任何东西。
然而,对于普通开发人员的日常项目,使用常量接口是一个容易得多,因为你不必操心static
,final
,empty constructor
等,并不会造成任何不良的设计问题。我能想到的唯一缺点是它仍然具有“接口”的名称,仅此而已。
最后,我认为每个人都只是在引用书本,并为自己的立场提供意见和理由。我也不例外。也许决定仍然取决于每个项目的开发人员。如果您感到舒适,请继续使用它。我们能做的最好的事情就是在整个项目中保持一致。
public
也可以省略,因为它是一个接口,从而使其简单地double PI = 3.14159;
使用Constants.PI
不需要使用该修饰符的类来实现Constants
接口!我认为接口方法在用法方面更加
Joshua Bloch,“有效的Java-编程语言指南”:
恒定接口模式是对接口的不良使用。类内部使用一些常量是一个实现细节。实现常量接口会导致此实现细节泄漏到类的导出API中。对类的用户而言,该类实现一个常量接口并不重要。实际上,它甚至可能使他们感到困惑。更糟糕的是,它表示一种承诺:如果在将来的版本中对该类进行了修改,使其不再需要使用常量,则它仍必须实现该接口以确保二进制兼容性。如果非最终类实现了常量接口,则其所有子类的名称空间都会受到接口中常量的污染。
如果您具有将在实现接口的类中使用的公共常数,则它们将非常有用。
这是一个示例:http : //www.javapractices.com/topic/TopicAction.do?Id=32
但是请注意,建议的做法是在接口中使用静态导入而不是常量。这是参考:http : //www.javapractices.com/topic/TopicAction.do?Id=195
关于界面的两点:
接口描述了实现它的对象可以做什么的子集。(这是直觉)
接口描述公共常量,后跟实现该接口的对象。
因此,我认为如果Constants Interface不用于全局常量,则可以接受:
implements
可以了(当然,在实现中使用这些公共常量)。例:
interface Drawable {
double GOLDEN_RATIO = 1.618033988;
double PI = 3.141592653;
...
// methods
...
}
public class Circle implements Drawable {
...
public double getCircumference() {
return 2 * PI * r;
}
}
void usage() {
Circle circle = new Circle(radius: 3.0);
double maxRadius = 5.0;
if ( circle.getCircumference() < 2 * Circle.PI * maxRadius ) {
...
}
}
在此示例中:
Circle implements Drawable
,您立即知道它Circle
可能符合中定义的常量Drawable
,否则它们必须选择一个较差的名称,因为好的名称PI
和GOLDEN_RATIO
已经采取的!Drawable
对象符合中定义PI
和GOLDEN_RATIO
定义的Drawable
对象,才能Drawable
存在pi和黄金分割率精度不同的对象。该javax.swing.SwingConstants
接口是一个示例,该示例获取了在swing类之间使用的静态字段。这使您可以轻松使用类似
this.add(LINE_START, swingcomponent);
this.add(this.LINE_START, swingcomponent);
要么 this.add(SwingComponents.LINE_START, swingcomponent);
但是此接口没有方法...
我遇到了这个问题,以为我会添加未提及的内容。总的来说,我在这里同意Pascal的回答。但是,我不认为接口上的常量“总是”是反模式的。
例如,如果您要定义的常量是该接口的合同的一部分,那么我认为接口是常量的理想之选。在某些情况下,在不将合同暴露给实现的用户的情况下私下验证您的参数是不合适的。没有公开的合同,用户只能猜测您的验证内容,而无需反编译类并阅读您的代码。
因此,如果您实现一个接口并且该接口具有用于确保合同的常量(例如整数范围),则该类的用户可以通过检查接口中的常量来确保他们正确使用了接口实例他们自己。如果常量是您的实现私有的,或者您的实现是私有的,则这是不可能的。
在处理类之间的共享常量时,我使用接口常量。
public interface TestConstants
{
String RootLevelConstant1 = "RootLevelConstant1";
interface SubGroup1
{
String SubGroupConstant1 = "SubGroup1Constant1";
String SubGroupConstant2 = "SubGroup1Constant2";
}
interface SubGroup2
{
String SubGroupConstant1 = "SubGroup2Constant1";
String SubGroupConstant2 = "SubGroup2Constant2";
}
}
分组是一项巨大的资产,尤其是在具有大量常量的情况下。
要使用,只需将它们链接在一起:
System.out.println(TestConstants.SubGroup1.SubGroupConstant1);
System.out.println(TestConstants.SubGroup2.SubGroupConstant1);
System.out.println(TestConstants.RootLevelConstant1);
应该在接口中声明字段,以便它们更易于共享,并且可以在不引入额外耦合的情况下进行引用。
资料来源:Java开发人员工具编码风格