Global constants aren't bad practice, as long as they are...
- ... immutable - a global,
final
/readonly
reference to a mutable object (like a Java ArrayList<T>
or a C# List<T>
) is not a constant, but global state.
- ... needed by >1 class. If only one class needs your constants, put the constants directly in the class. (Caveat: Balance DRY vs YAGNI appropriately.)
Bloch covers the "constant interface" vs. "constant class" issue in Effective Java, and advocates the "constant class" approach. The reason why you don't want the constants in an interface is that it entices client classes to "implement" that interface (in order to access the constants without prefixing them with the interface name). You shouldn't, though - the interface isn't actually an interface to the object's capabilities, but a compile-time convenience ingrained in the class' external type. Consider this:
interface C { public static final int OMGHAX = 0x539; }
class A implements C { ... }
class B { private A a; }
Class B
now unnecessarily has a dependency to C
. If the implementation of A
changes so that it doesn't need the constants from C
, you can't remove implements C
from it without breaking its external interface - someone (arguably a very stupid person, but such people abound) might reference an A
object through a C
reference!
By putting the constants in a class, and by making that class uninstantiable, you inform clients that the constant class really just functions as a sub-namespace. In C# you mark the class as static
, in Java you'd want to make it final
and give an unreachable constructor:
final class C {
private C() { throw new AssertionError("C is uninstantiable"); }
public static final int OMGHAX = 0x539;
}
If you program in Java and want the constants without prefixing them with the constant class name, you can use the import static
functionality.
And yes, it's slightly redundant to be forced to create a new type just to have somewhere to put your constants, but that's a wart in languages like Java and C# that we have to deal with - we have to put our constants somewhere, and our best option happens to be a non-instantiable class.