The idea behind encapsulation in object-oriented programming is moving data and the code that uses it together.
There is no "but the data are constants, treat them specially." If you have colors, keep them in a Color
class. Java's Calendar
class has calendar field constants in it. BigDecimal
and BigInteger
each contain constants of their own type.
The idea is that referencing constants involves their scope: BigDecimal.ZERO
for example. I know that is a decimal zero because I typed BigDecimal.ZERO
and not BigInteger.ZERO
.
One of the worst anti-patterns in this regard is having a "constant interface" which is honestly lazy and unclear. Sometimes you come across an interface containing a bunch of random constants thrown together, and classes will implement the interface just to make it easy to refer to the constants. That generally ends up violating LSP and makes it difficult to determine what those constants do.
Constants should be:
An enum if appropriate, such as java.math.RoundingMode
. The Java Calendar fields which were created before enum
existed should be an enum. This necessarily scopes them properly and ensures other constants are not in the same namespace (class static final field).
Located on the class that defines their type, if appropriate. This covers the reusable numbers on BigDecimal
for example.
Located somewhere else where like constants are together and not intermingled with other, unlike constants.
One could argue this is a matter of opinion. I feel that if you asked any group of experts to compare e.g. BigDecimal
and some random AWT constants class, we would come to a consensus that there are some really bad practices in reality and some that make code clearer and easier to understand.