Recently, I was writing a test method similar to this:
/**
* SOME_FEATURE is a constant that identifies a feature the application provides if enabled
*/
void transferMoneyTest() {
withEnabledFeature(SOME_FEATURE, () -> {
// line one of the test method
// line two of the test method
// line three of the test method
// line four of the test method
// line five of the test method
// line six of the test method
// line seven of the test method
});
}
One of my colleagues pointed out that it is a best practice to place lambda code in a distinct method when its size exceeds a certain threshold (e.g., 2 or 3 lines). Hmm, I thought to myself, he is right, but there are exceptions – and this is one of them.
But why is that an exception? Well, let’s see other similar examples:
/**
* "transactional" method provides the ACID transaction context for the lambda
* execution (is the equivalent of e.g., @Transactional from Spring Framework)
*/
public void transferMoney() {
transactional(() -> {
// line one of the method's implementation
// line two of the method's implementation
// line three of the method's implementation
// line four of the method's implementation
// line five of the method's implementation
// line six of the method's implementation
// line seven of the method's implementation
});
}
/**
* "preAuthorize" method provides the security context for the lambda
* execution (is the equivalent of e.g., @PreAuthorize from Spring Security)
*/
public void transferMoney() {
preAuthorize("hasAuthority('permission:write')", () -> {
// line one of the method's implementation
// line two of the method's implementation
// line three of the method's implementation
// line four of the method's implementation
// line five of the method's implementation
// line six of the method's implementation
// line seven of the method's implementation
});
}
/**
* Let's suppose it doesn't make sense to use *synchronized* at the method level.
*/
public void transferMoney() {
synchronized (merchant) {
// line one of the method's implementation
// line two of the method's implementation
// line three of the method's implementation
// line four of the method's implementation
// line five of the method's implementation
// line six of the method's implementation
// line seven of the method's implementation
});
}
All the above examples show some code blocks executed in a particular *context*.
Would the code be more readable if one took the below approach instead of a lambda expression?
public void transferMoney() {
transactional(doTransferMoney);
}
// ...
// a
// bunch
// of
// other
// public,
// protected,
// and
// private
// methods
// go
// here
// (i.e., there's a big distance between transferMoney and doTransferMoney)
// ...
private void doTransferMoney() {
// line one of the method's implementation
// line two of the method's implementation
// line three of the method's implementation
// line four of the method's implementation
// line five of the method's implementation
// line six of the method's implementation
// line seven of the method's implementation
}
Well, the transferMoney is much shorter and hence easier to read; the problem though is with *what* is left to read:
the user reads a method meaning “transfer money” (i.e., transferMoney) which does “transfer money” (i.e., doTransferMoney) in a transactional fashion (well, at least he learns something about the *execution context*).
This is silly; in the above situations, I would rather keep the doTransferMoney content in transferMoney method because it’s easy to spot the distinction between the *execution context* and the *behaviour* implementation; moving the *behaviour* in another method brings no benefit.
This does not invalidate the rule of thumb that a lambda expression larger than 2-3 lines is best to be moved to a distinct method. In most situations, the *behaviour* is mixed with the lambda expressions hence the reader has to collapse the lambda expression to see the rest (i.e., the *behaviour*). It’s not the same for transferMoneyTest and the similar examples above, because collapsing the lambda expression means hiding the *behaviour* the user intends to view.