Consider the following:
public boolean maybeUpdateTime() {
if (this.timeReference.isAfter(lastInterval.getBeginning()) {
this.timeReference = lastInterval.getEnd();
lastInterval = intervalIterator.next();
return true;
}
return false;
}
public void updateState() {
// do some stuff before
while (maybeUpdateTime());
// do more stuff afeter
}
Which roughly translates to: while it needs to be updated – update it.
Considering that maybeUpdateTime
is a function/method that can be called independently, would it still be considered a bad practice to write such an empty while?
Otherwise I would have to create two functions, isTimeAfterInterval
and updateTime
, and call both every time.
I used this time and interval-based example just to illustrate, but the specifics don't matter to my question.
Note that maybeUpdateTime
is a "public" function that can be called independently of updateState
and not always inside a loop until it returns false like there.
Therefore, it is important to wrap it inside a function of its own. This code is an example and thus I kept it simple, but it could be a much bigger computation.
Best Answer
From a code-review perspective - the intuitive process
The function
maybeUpdateTime
does not seem to be problematic. However, it definitely needs a better name, one that conveys information to a programmer succinctly.The while loop inside the
updateState
function may trigger a false-alarm for a programmer who is reading the code. That is, it makes the reader feel scary, and compels the reader to fetch more information to assure that it is not a bug.When a programmer reads a
while
loop and tries to understand what it does, there are several pieces of information that the programmer needs to grasp (or guess):Altogether, these thought processes increased the cognitive load of the person reading the code (especially the
while
loop). If one or more issues are fixed, the cognitive load will decrease, and thewhile
loop will become less scary to the other programmer.From a code-review perspective - objections that may be raised
Note: this section dissects into the code example provided by OP. As OP explains, such detail is not the main focus of this question.
In the code sample above, changes are made to two instance fields (of the
this
object). The function's name will need to communicate either the how or the why these field changes are made.But how do we explain the why?
We can only explain in a manner that makes sense to fellow humans. For this reason, we (humans) create idioms and analogies, drawing from experience from our everyday, non-software-related lives.
We also try to separate a complex process into orthogonal, composable concerns.
It should be obvious that the code sample above is taken from a class that has certain iterator-like behavior. It is also obvious that the class has other responsibilities as well.
Thus, the first change we'd make is to isolate the iterator-like behavior - to make it an iterator that is extensible, without bundling with the additional responsibility. This is called the Single Responsibility Principle (SRP).
The sample code may have more than one use cases. Some examples are:
In each of the use case, it would help if the code is refactored (restructured) so that the what is being computed is made obvious, and separate from the act of iterating through.
From a coding style perspective
Given OP's desire to focus on the tension between:
In this case, the advise is to wrap that function so that the wrapper's name conveys the missing context.
Illustrated solution: