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.