Lately I've found myself inserting magic numbers into code to make it more readable. I've done this in situations where the magic number is only used once and its purpose is obvious from the context. An example from a recent project:
/* Extract id from "/toClient/chat/id". */ String channelId = channelIdWithPath.split("/");
Apparently, the "best practice" is to declare the magic number as a constant near the beginning of the class, like this:
private final int NUMBER_OF_DELIMITERS_BEFORE_ID_IN_CHAT_CHANNEL_PATH = 3; ... 100 lines of unrelated code ... /* Extract id from "/toClient/chat/id". */ String channelId = channelIdWithPath.split("/")[NUMBER_OF_DELIMITERS_BEFORE_ID_IN_CHAT_CHANNEL_PATH];
I can't see any advantage in separating the variable declaration from the location where it's used. We've practically doubled the time it takes to understand this code and anyone reading it has to jump over 100 lines to confirm the variable has the correct value.
We could declare it as a local variable right before it's needed, but that's still a "magic number" at least according to CheckStyle. This is something I do often when I feel like the number needs a description (in this case a description like "number of delimiters..." is harder to understand than just the raw number at use).
Another option is to separate manipulation logic into a function:
String channelId = getIdFrom(channelIdWithPath);
This abstraction hides the details of the String manipulation and removes the need for a comment. Unfortunately we still have to write the function somewhere and the original problems are replicated there (including the need to document an example path with a comment).
If we want to declare the variable as a constant AND keep it close to the function, we need to create some kind of StringManipulator class for it. Now we've added a class to do something that takes 1 line of code. I feel like this type of approach leads to sprawling programs where
- the individual components of a program become easier to understand
- the structural complexity and execution flow become harder to understand.
For example, if I wanted to read how channelId is retrieved, I would first have to jump into another method in another class, and then I would have to jump to where the variable is declared. All of this could be on a single line of code.
Edit: this answer on a similar question applies pretty well for my particular question.