恒定界面反模式澄清

时间:2015-04-01 04:13:01

标签: java

我刚读到某个地方有一个带有常见项目常量的界面是不好的做法,也被称为Constant Interface Anti-Pattern。如果我理解正确,那么提供的理由是,一旦实现,该类就会将这些常量暴露给公众。

好吧,我不明白是否需要实施'首先。是不是可以直接使用这些静态常量?那么,为什么我必须经历import static的麻烦,我可以做类似的事情:

interface Constants {
    public static final int FOO_1 = 1;
    public static final int FOO_2 = 2;
}

public class Test {
    public static void main(String[] args) {
        System.out.println(Constants.FOO_2);
    }
}

我很感激任何指导,以帮助我理解这一点。

4 个答案:

答案 0 :(得分:30)

反对"常量接口模式"主要是风格。如果符合您的需要,可以使用Java中的常量接口,实际上Java库包含其中的一些(尽管它们被认为是不应该重复的不良示例)。

许多人将常量接口视为"反模式"在Effective Java,2nd Ed中列举。简而言之,不鼓励使用接口的一些原因包括:

  • 命名空间污染。命名常量出现在所有实现类的名称空间及其子类中。

  • 接口应定义类型。在Java中,项目中的大多数主要类型都应该由接口表示。根据其性质,常量接口不定义类型。

  • import static的不可实例化的类。将常量声明为类(而不是接口)中的静态最终字段可实现与在接口中声明它们相同的所有目标。这样做不会导致类的命名空间污染。如果需要,可以使用import static声明在没有限定类名的情况下使用这些常量。

  • 接口应指定行为。接口应该定义接口和实现类之间的契约。实现接口应该说明类可以做什么。常量接口不遵循这种模式。

答案 1 :(得分:21)

我意识到......如果需要,接口可以由个人实现,这为上面指出的问题留下了空间(即命名空间污染,非常规使用,通过公共API暴露)。因此,最好不要完全阻止实现界面的能力。因此,使用私有构造函数的final类更合适,这样它就不能被实例化/扩展。

public final class Constants
{
      // to restrict instantiation
      private Constants() {}

      public static final double PI = 3.14159;
      public static final double PLANCK_CONSTANT = 6.62606896e-34;
}

...并将其与import static结合使用。

import static Constants.PLANCK_CONSTANT;
import static Constants.PI;

public class Calculations
{   
      public double getReducedPlanckConstant()
      {       
            return PLANCK_CONSTANT / ( 2 * PI );   
      }
}

答案 2 :(得分:6)

那不是pattern。它更像是:

interface Constants {
    final int FOO_1 = 1;
    final int FOO_2 = 2;
}

public class MyClass implements Constants {
    public static void main( String[] args ) {
        System.out.println( FOO_2 ); // compiles OK
    }
}

恕我直言,问题是MyClass“不是”Constants。该模式使用了一种可见性技巧,但却掩盖了该类的意图。此外,字段 shadowing 可以在没有编译器警告的情况下发生 - 这更可能是因为您看到 all 接口的字段,即使您不全部使用它们。

import static com.foo.Constants.*;最好能够实现相同的编码方便性,而不会误导成为Constants的成员。

答案 3 :(得分:1)

我们可以同时使用这两个类(最终私有构造函数)和接口。

但是由于以下原因,我更喜欢上课:

  1. 接口及其实现类应具有 IS A 关系。 例如:“Cat延伸哺乳动物”意味着猫是哺乳动物。如果我们实现在接口中使用的常量,则不适用。

  2. 界面应定义类型

  3. 但是现在我们也可以将静态导入与接口一起使用,但实施它的人有可能违反“IS A”关系。

  4. 为常量创建一个类(具有以下属性)并使其成为:

    1. 最终 - 因此其他类无法继承
    2. 只有一个私有构造函数: - 因此无法实例化
    3. PS :复制用过的示例

      public final class Constants  // so it cannot be inherited
      {
      
        private Constants() {} // to restrict instantiation
      
        public static final double PI = 3.14159;
        public static final double PLANCK_CONSTANT = 6.62606896e-34;
      }
      

      现在用法:

      import static Constants.PLANCK_CONSTANT;
      import static Constants.PI;
      
      public class Calculations
      {   
        public double getReducedPlanckConstant()
        {       
              return PLANCK_CONSTANT / ( 2 * PI );   
        }
      }