Constant Interface Anti-Pattern Clarification
Asked Answered
S

4

35

I just read somewhere that having an interface with common project constants is bad practice and also known as the Constant Interface Anti-Pattern. If I understood this correctly, the reason provided was that once implemented, the class would expose these constants to the public.

Well, I don't understand the need for 'implementing' in the first place. Isn't it possible to just use these static constants directly? So why do I have to go through the trouble of import static when I can do something like:

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);
    }
}

I would appreciate any guidance to help me understand this a bit more.

Sacrifice answered 1/4, 2015 at 4:13 Comment(2)
anti-pattern or not. That is your decision and not from someone else. I use constant interface very often.Female
final classes can be instantiated but cannot extendedHavenot
S
30

I realised... the fact that the interface CAN be implemented by an individual if desired, leaves room for the issues pointed out above (i.e. namespace pollution, non-conventional use, exposure through public API ). So it's best to prevent the ability to implement the interface altogether. Hence, it's more appropriate to have a final class with a private constructor so that it can't be instantiated / extended.

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;
}

... and use that in combination with the import static.

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

public class Calculations
{   
      public double getReducedPlanckConstant()
      {       
            return PLANCK_CONSTANT / ( 2 * PI );   
      }
}
Sacrifice answered 1/4, 2015 at 7:39 Comment(4)
+1 for answering your question when other answers seem to have missed the point. Note that the best way to implement constants is often an enum rather than a final class.Robinrobina
Note that although enum does bring type safety, it's still instantiating and keeping potentially large objects in memory - and is discouraged on some platforms, like Android for example.Antecede
There are exceptions. Have a look at android.provider.BaseColumns. It's an interface with only two constants.Cranach
@jaco0646: "...the best way to implement constants is often an enum rather than a final class." ... No argument. In fact, this is semantically identical to declaring static final constants in a class and then importing them with import static.Jillianjillie
J
45

The arguments against the "Constant Interface Pattern" are mainly stylistic. You can use a Constant Interface in Java if it suits your need and in fact the Java libraries include a few of these (though they are considered poor examples that shouldn't be repeated).

The reasons why the Constant Interface is considered by many to be an "anti-pattern" are enumerated in Effective Java, 2nd Ed. Briefly, some of the reasons that this use of interfaces are discouraged include:

  • Namespace pollution. The named constants appear in the namespace of all implementing classes as well as their subclasses.

  • Interfaces should define types. In Java, most of the major types in a project should be represented by interfaces. A constant interface by its nature does not define a type.

  • Noninstantiable classes with import static. Declaring constants as static final fields in a class (rather than an interface) achieves all the same objectives as declaring them in an interface. Doing so does not create namespace pollution by the class. If desired, these constants can be used without the qualifying class name by using the import static declaration.

  • Interfaces should specify behavior. An interface is supposed to define a contract between the interface and implementing classes. Implementing the interface is supposed to say something about what the class can do. Constant interfaces do not follow this pattern.

Jillianjillie answered 1/4, 2015 at 4:52 Comment(2)
Excellent points but I have found an exceptions. If you look into android.provider.BaseColumns you will find an interface with only two constants (_id and _count). This was done my the Android developers so I guess it can not be that bad.Cranach
"This was done by[sic] the Android developers so I guess it can not be that bad. " ... it just depends on what you understand 'bad' to be. As I said, constant interfaces are a style choice (and there are some in the Java libraries). Use them all you want. The reasons not to use them are quite cogent, tho.Jillianjillie
S
30

I realised... the fact that the interface CAN be implemented by an individual if desired, leaves room for the issues pointed out above (i.e. namespace pollution, non-conventional use, exposure through public API ). So it's best to prevent the ability to implement the interface altogether. Hence, it's more appropriate to have a final class with a private constructor so that it can't be instantiated / extended.

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;
}

... and use that in combination with the import static.

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

public class Calculations
{   
      public double getReducedPlanckConstant()
      {       
            return PLANCK_CONSTANT / ( 2 * PI );   
      }
}
Sacrifice answered 1/4, 2015 at 7:39 Comment(4)
+1 for answering your question when other answers seem to have missed the point. Note that the best way to implement constants is often an enum rather than a final class.Robinrobina
Note that although enum does bring type safety, it's still instantiating and keeping potentially large objects in memory - and is discouraged on some platforms, like Android for example.Antecede
There are exceptions. Have a look at android.provider.BaseColumns. It's an interface with only two constants.Cranach
@jaco0646: "...the best way to implement constants is often an enum rather than a final class." ... No argument. In fact, this is semantically identical to declaring static final constants in a class and then importing them with import static.Jillianjillie
C
13

That's not quite the pattern. It's more like:

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
    }
}

IMHO, the issue is that MyClass "is not a" Constants. The pattern uses a trick of visibility, but clouds the intention of the class. Further, field shadowing can occur without compiler warnings - this is all the more likely because you see all the fields of the interface, even if you don't use them all.

It's better to import static com.foo.Constants.*; to achieve the same coding convenience, without the misdirection of being a member of Constants.

Cephalochordate answered 1/4, 2015 at 4:34 Comment(0)
E
2

We can use both class(final with private constructor) and Interface.

However I would prefer class due to the following reasons:

  1. Interface and its implementing classes should have a IS A relationship. Eg: "Cat extends Mammal" means Cat is a Mammal. This doesn't hold true in case we implement constants we use in interfaces.

  2. Interface should define a type.

  3. However now, we can use static import with Interface as well but there is a risk with someone implementing it which will violate "IS A" relationship.

Have a class (with following properties) for constants and make it:

  1. FINAL - so it cannot be inherited by other classes.
  2. Having only a private Constructor :- so it cannot be instantiated.

PS: copying the used example

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;
}

Now the usage:

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

public class Calculations
{   
  public double getReducedPlanckConstant()
  {       
        return PLANCK_CONSTANT / ( 2 * PI );   
  }
}
Emmott answered 5/2, 2018 at 14:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.