This is indeed inadequate and should be flagged in review. To understand why, let's step back.
What's in a variable?
A variable (or constant) is an instance (value) of a given type. It conveys a variety of pieces of information through both its name and its type.
A variable is, first and foremost, a value. In your case, the underlying value is 3.
A variable with a number as an underlying value generally represents a quantity. A quantity is often the association of a number and a unit, though there are unitless quantities such as the number of iterations in a loop.
The unit can be conveyed through either type or name:
public static int THREE_MS = 3;
public static Milliseconds THREE = new Milliseconds(3);
Although the unit itself is not as important as the dimension:
public static Duration X = Duration::from_milliseconds(3);
It is notable that just because 2 variables have the same dimension does not mean that they interchangeable, and therefore it can be valuable to further refine a type-hierarchy -- when such a thing exists -- to represent specific kinds of values. This refinement can also be applied to unitless quantities, such as indices on a board: a row index is not a column index, and vice-versa.
Finally, a variable typically has a purpose which further restricts its applicability. You would not want to use the heartbeat_interval
instead of the polling_interval
, for example.
Applying to your example.
In general, I'd recommend employing a mix of type and name.
I myself prefer to err on the side of types, wherever practical, and routinely create "wrapper" types. In the above example, should I create a Board, I would have:
class RowIndex(int);
class ColumnIndex(int);
class Cell(...);
class Board(...);
function Board.get(RowIndex row, ColumnIndex column) -> Cell;
And this would help avoiding accidentally swapping one index for another.
I do use names, still, and thus going back to your previous example:
public static Duration REQUEST_INTERVAL = Duration::from_milliseconds(3);
Would be my favorite:
- A dimension type precisely conveys the dimension of the quantity.
- By using a dimension type, we can also precisely convey the unit of the number, at its point of construction.
- Finally, the name conveys the purpose.
On the point of the name, note that I did not name it REQUEST_INTERVAL_MS
nor REQUEST_INTERVAL_DURATION
, there is not point in repeating in the name information provided by the type.
Applying to the original example.
The original (linked) example is:
int seconds = days * 24 * 60 * 60;
The error, here, is the Primitive Obsession. Using meaningful types allows having meaningful names.
As an example, C++ std::chrono::duration<...>
can express both days and seconds resolution, it uses magic numbers in its formation, where the intent is clear, and never again:
using nanoseconds = duration<int64_t, std::ratio<1, 1'000'000'000>>;
using microseconds = duration<int64_t, std::ratio<1, 1'000'000>>;
using milliseconds = duration<int64_t, std::ratio<1, 1'000>>;
using seconds = duration<int64_t, std::ratio<1, 1>>;
using minutes = duration<int64_t, std::ratio<60, 1>>;
using hours = duration<int64_t, std::ratio<3600, 1>>;
using days = duration<int64_t, std::ratio<86400, 1>>;
And from there I can do:
auto const x = seconds{ days{ 3 } };
To have x
be a duration expressed in seconds initialized by a number of days.
THREE= 4
which is even less meaningful.int THREE_MINUTES = 180;
be wrong if the system does everything in seconds? The problem here is not the missing unit, but the wrong type -THREE_MINUTES
isn't a number, it's a time span.THREE = 3
toTHREE = 4
, you change it toFOUR = 4
and then fix all the code that fails to compile becauseTHREE
no longer exists. Just kidding,THREE
should have had a meaningful name in the first place.