Lambda expressions with many lines

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.