Constants in Java: how to avoid anti-Pattern
In applications, we often need a constant file to store shared constants referenced by multiple places. When designing an application, I also encountered a similar situation where various constants are needed in many places.
I'm sure I need a separate file to store these static public constants. But I am not particularly sure whether the interface or class enumeration cannot meet my needs ). I have two options:
Use interfaces, such:
1 2 3 4 5 |
package one; public interface Constants { String NAME= "name1" ; int MAX_VAL= 25 ; } |
Or
1 2 3 4 5 |
package two; public class Constants { public static final String NAME= "name1" ; public static final int MAX_VAL= 25 ; } |
My point is to use interfaces. Because the interface will automatically set the member variables to static) and immutable final), this can prevent adding new constants incorrectly in some cases. This also makes the Code look simpler and clearer.
At the same time, a simple test shows that the space occupied by the same interface bytecode file is 209 bytes on ubuntu 14.04), while the class bytecode file) the occupied space is 366 bytes of the same operating system ). Smaller bytecode files mean lower loading and maintenance costs. In addition, when the JVM loads an interface, it does not need to worry about additional features provided by the class, such as overloading and dynamic binding of methods. Therefore, loading is faster.
This looks very good, but this is a typical anti-pattern example. Although it is helpful to use interfaces to save constants, this leaves a vulnerability for later application extensions.
Suppose there is a class closely dependent on these constants. The developer fills the class with reference to constants through interfaces. For example:
1 |
packagename.Constant.CONSTANT_NAME |
Therefore, to "Clean up" the Code, he may want to implement this interface, so that he does not need to write "packagename. Constants" everywhere, and all Constants can be directly accessed.
However, once this interface is implemented, all constants become "contracts" because all constants are public and static. This causes unnecessary constants to be added to this class. This will shake the entire infrastructure and cause confusion. There is no way in Java to block class implementation interfaces.
In another way, we can set the class to final, so that it cannot be extended. Even we can set the constructor to private to prevent instantiation of this class, so that the conventions will never be broken. In addition, if a special constant is used multiple times in the same class, developers can use static introduction.
For all constant classes, a better design should be:
1 2 3 4 5 6 7 |
package three; // Make the class non-extendable by adding final add the final keyword to avoid inheritance public final class Constants { // Hide the constructor hides the constructor private Constants(){} public static String NAME= "name" ; } |
Example of static introduction:
1 2 3 4 5 6 |
import static three.Constants.NAME; public class UseConstants { public static void main(String[] args) { System.out.println( "the value of constants is" +NAME); } } |
This design problem is also called the Interface Constant Anti-pattern Constant Interface Anti-pattern ).