My Latest Programming-Technique Improvements
Tue Apr 23 11:34:58 EDT 2013
Over the past couple weeks, I've been trying out some new things to make my code a bit cleaner and I've been having a good time of it, so I figured I'd write up a quick summary.
Composed Method
First and foremost, I've been trying out the Composed Method of programming. This is not a new idea - indeed, it's a pretty direct application of general refactoring - but the slight perspective change from "factor out reused code" to starting out with clean, small blocks has been a pleasant switch. The gist of the idea is that, rather than writing a giant, LotusScript-agent-style block of code first and then breaking out individual components after the fact, you think in terms of small, discrete operations that you can name. The result is that your primary "control" methods end up very descriptive and easy to follow, consisting primarily of ordered phrases specifically saying what's going on. I encourage you to search around and familiarize yourself with the technique.
import static
Java's import static
statement is a method-level analog to the normal import
statement: it lets you make static methods of classes available for easy calling without having to specify the surrounding class name. Most of the examples you'll see (like those on the linked docs) focus on the Math
class, which makes sense, but it applies extremely well to XPages programming. Pretty much every app should have a JSFUtil
class floating around, and most should also use the ExtLibUtil
class for its convenience methods like getCurrentSession
:
import static com.ibm.xsp.extlib.util.ExtLibUtil.getSessionScope; import static com.ibm.xsp.extlib.util.ExtLibUtil.getCurrentSession; // ... getSessionScope().put("currentUser", getCurrentSession().getEffectiveUserName());
Night and day? Not really, but it's a little more convenient and easier to read.
Now, as the linked docs above indicate, import static
comes with a warning to use it cautiously. Part of this is Java's cultural aversion to concision, but it's also a good idea to not go too crazy. When the methods you're importing are clear in intent and unlikely to overlap with local ones (like those in ExtLibUtil), it makes sense, particularly because Designer/Eclipse allows you to hover over the method or hit F3 and see where it's defined.
createDocument
Since creating a document in the current database with a known set of fields is such a common action, I've taken a page from one of our extended methods in org.openntf.domino and have started using a createDocument
method that takes arbitrary pairs of keys and value and runs them through replaceItemValue, along with a helper method to allow for storing additional data types (since I'm still using the legacy API for these projects):
protected static Document createDocument(final Object... params) throws Exception { Document doc = ((Database)resolveVariable("database")).createDocument(); for(int i = 0; i < params.length; i += 2) { if(params[i] != null) { String key = params[i].toString(); if(!key.isEmpty()) { doc.replaceItemValue(key, toDominoFriendly(params[i+1])); } } } return doc; } protected static Object toDominoFriendly(final Object value) throws Exception { if(value == null) { return ""; } else if(value instanceof List) { Vector<Object> result = new Vector<Object>(((List<?>)value).size()); for(Object listObj : (List<?>)value) { result.add(toDominoFriendly(listObj)); } return result; } else if(value instanceof Date) { return ((Session)resolveVariable("session")).createDateTime((Date)value); } else if(value instanceof Calendar) { return ((Session)resolveVariable("session")).createDateTime((Calendar)value); } return value; }
In use, it looks like this:
Document request = createDocument( "Form", "Request", "Principal", getCurrentSession().getEffectiveUserName(), "StartDate", getNewRequestStartDate(), "EndDate", getNewRequestEndDate(), "Type", getNewRequestType() );
The result of these changes is that I'm writing less code and the intent of every line is much more clear. It also happens to be more fun, which never hurts.
David Leedy - Wed Apr 24 18:45:39 EDT 2013
Excellent post! For both tips. I did something similar to your createDocument code in SSJS by passing in a hashMap I think of fieldName and fieldValue but this looks so much better and more robust.
did you add this to XSnippets by any chance?
Jesse Gallagher - Thu Apr 25 10:13:43 EDT 2013
I actually thought of adding it to XSnippets when I woke up at 4 AM today. I don't like what that says about my brain, but it's still a good idea to do so, after I add an extra sanity check or two to the implementation.
I wish Java had a literal hash syntax - using varargs like this, while it works, is sort of an abuse of syntax, since there's nothing that enforces having an even number of parameters or having proper key/value pairs like a literal would. Still, it's worth using.