It might seem obvious, but recently I saw a colleague use Optional in a weird way.
When I see a piece of code that seems to contain about 3 or more Optional.isPresent() if checks and Optional.get() method calls, I kind of panic.
To be absolutely fair, the code wasn't ready yet, so was going to change anyways.
So, anyways, before we had Optionals, we had to deal with NULL a lot.
The way to do this was as follows:
private boolean documentAccessAllowed() {
Information information = session.getInformation();
if (information != null) {
Document document = database.getDocument(information.getDocumentnumber());
if (document != null) {
if (!document.getOwner().equals(user)) {
Administrator administrator = database.getAdmininstrator(user.getName());
if (administrator == null) {
addErrorMessage("You are not allowed access to this document.");
return false;
}
}
}
}
return true;
}
I realize this code could be refactored using "Replace Nested Conditional with Guard Clauses", but some purists frown upon multiple return statements in a method. I don't mind. But let's continue with the example as is.
Then we got Optionals, yay! But, translating the above code verbatim causes my head to explode:
private boolean documentAccessAllowed() {
Optional<Information> information = session.getInformation();
if (information.isPresent()) {
Optional<Document> document = database.getDocument(information.get().getDocumentnumber());
if (document.isPresent()) {
if (!document.get().getOwner().equals(user)) {
Optional<Administrator> administrator = database.getAdmininstrator(user.getName());
if (administrator.isEmpty()) {
addErrorMessage("You are not allowed access to this document.");
return false;
}
}
}
}
return true;
}
This code is of course hardly ideal!
A better way is to use the flatMap and map and filter functions of Optional, which seems more concise, but requires a bit of a mental adjustment.
private boolean documentAccessAllowed() {
Boolean allowed = session.getInformation()
.map(Information::getDocumentnumber)
.flatMap(database::getDocument)
.filter(doc -> doc.getOwner().equals(user)) || database.getAdmininstrator(user.getName()).isEmpty())
.isPresent();
if (!allowed) {
addErrorMessage("You are not allowed access to this document.");
}
return allowed;
}
The advice of a colleague of mine is that lambdas, even slightly trivial ones, are a bit hard to read, and making it a method with a good name helps clear things up immensely, like so:
The idea here is not to make the code short (although that most often helps) but make the code very easy to read and follow.
private boolean documentAccessAllowed(Session session, Database database, User user) {
boolean allowed = session.getInformation()
.map(Information::getDocumentnumber)
.flatMap(database::getDocument)
.filter(doc -> isOwnerOrAdministrator(database, user, doc))
.isPresent();
if (!allowed) {
addErrorMessage("You are not allowed access to this document.");
}
return allowed;
}
private static boolean isOwnerOrAdministrator(Database database, User user, Document doc) {
return isOwner(user, doc) || isAdministrator(database, user);
}
Let me know if you have any suggestions, of which there are no doubt several, on how this can be improved in readability.