Describe a task, either with a comment or inherently due to the structure of your code and the identifiers chosen, and then use code blocks to create a hierarchical relationship there where the language itself doesn't enforce one. For example:
public void sendAdminMessage(String msg) throws IOException {
MessageService service; {
String senderKey = properties.get("admin-message-server");
service = MessageService.of(senderKey);
if (!ms.available()) {
throw new MessageServiceException("Not available: " + senderKey);
}
}
/* workaround for issue 1298: Stop sending passwords. */ {
final Pattern p = Pattern.compile("^(.*?)\"pass\":.*(\"stamp\".*)$");
Matcher m = p.matcher(msg);
if (m.matches()) msg = m.group(1) + m.group(2);
}
...
}
The above is just some sample code to explain the concept. The first block is 'documented' by what is immediately preceding it: That block serves to initialize the service
variable. The second block is documented by a comment. In both cases, the block provide 'scope' for the comment/variable declaration: They explain where that particular process ends. It's an alternative to this much more common style:
public void sendAdminMessage(String msg) throws IOException {
// START: initialize service
String senderKey = properties.get("admin-message-server");
MessageService service = MessageService.of(senderKey);
if (!ms.available()) {
throw new MessageServiceException("Not available: " + senderKey);
}
// END: initialize service
// START: workaround for issue 1298: Stop sending passwords.
final Pattern p = Pattern.compile("^(.*?)\"pass\":.*(\"stamp\".*)$");
Matcher m = p.matcher(msg);
if (m.matches()) msg = m.group(1) + m.group(2);
// END: workaround for issue 1298: Stop sending passwords.
...
}
The blocks are better, though: They let you use your editor tooling to navigate more efficiently ('go to end of block'), they scope the local variables used within the block so that they cannot escape, and most of all, they align the concept of containment: You are already familiar, as java programmer, with the concept of containment: for blocks, if blocks, method blocks: They are all expressions of hierarchy in code flow. Containment for code for documentary reasons instead of technical is still containment. Why use a different mechanism? Consistency is useful. Less mental load.
NB: Most likely the best design is to isolate the initialisation of the MessageService object to a separate method. However, this does lead to spaghettification: At some point isolating a simple and easily understood task to a method makes it harder to reason about method structure: By isolating it, you've turned the job of initializing the messageservice into a black box (at least, until you look at the helper method), and to fully read the code in order of how it flows, you need to hop around all over your source files. That's usually the better choice (the alternative is very long methods that are hard to test, or reuse parts of), but there are times when it's not. For example, if your block contains references to a significant number of local variables: If you make a helper method you'd have to pass all those variables. A method is also not control flow and local variable transparent (a helper method cannot break out of the loop from the main method, and a helper method cannot see or modify the local variables from the main method). Sometimes that's an impediment.