Before going onto that one more LotusScript function that is relevant to classes, there is another topic worth discussing with regard to classes. This is not functionality in LotusScript classes itself, but a design pattern which has been available ever since LotusScript began, but one which has become very commonly used in other languages as they have developed. It fits well after the discussion on using
Static for creating builder classes, because the builder pattern in Java is where it’s most widely used. The design pattern I’m referring to is a fluent interface.
A fluent interface is an object oriented API that relies extensively on method chaining. The benefits come when you’re wanting to call multiple functions in a row, it is easily readable and improves brevity in code. As I mentioned, it has become very common in Java builder classes. It’s a pattern I’ve used in LotusScript for a little while, first in Volt MX LotusScript Toolkit. The
NotesJsonArrayFluent provided fluent functions to speed up building JSON in a NotesJsonNavigator.
Creating the Fluent Function
The approach is very straightforward. Typically classes will have a Sub like
NotesDatabase.SetOption(), which don’t return anything. Or they have a Function like
NotesJSONNavigator.appendElement(passedElem) as NotesJsonElement which return the object being passed as a parameter. To convert this to a fluent function is straightforward, and done with
NotesJsonNavigatorFluent. The function signature is changed from
Function NotesJsonNavigator.appendELement(passedElem) as NotesJsonElement to:
Function NotesJsonNavigatorFluent.appendElementFluent(passedELem) as NotesJsonMavigatorFluent Call m_NotesJsonNavigator.appendElement(passedElem) 'm_NotesJsonNavigator = internal NotesJsonNavigator Set appendElementFluent = Me End Function
The function does whatever it would normally do, then returns the current object. This allows you to do
Improving The Code Further
That is good, but it’s not as easy to read as its Java equivalent would be. And what happens if there’s an error with one of the calls? Imagine the following code:
Class Foo Private elem List As String Function appendElem(key As String, value As String) As Demo Me.elem(key) = value Set appendElem = Me End Function End Class Class Bar Public myString As String End Class Sub Initialize() On Error GoTo errLog Dim myFoo as New Foo Dim myBar as Bar Call myFoo.appendElem("1", "One").appendElem("2", bar1.test) getOut: Exit Sub errLog: MsgBox "Error " & Error() & " on line " & Erl Resume getOut End Sub
This will throw an “Object variable not set” error on the line with
appendElem() calls. In this case, it’s easy to see it’s the second
appendElem() call produces the error. But it won’t always be so obvious. Fortunately, just as Java allows you to use separate lines for each method call in the chain, so does LotusScript. We just need to change the code like so:
Call myFoo.appendElem("1", "One")._ appendElem("2", bar1.test)
This throws an error where the error line is specifically the line with the second
appendElem() call. So we can immediately tell which part of the chain it failed on. And it also makes it more readable.
One caveat is that you need to understand what is happening when in the chain, and to avoid manipulating parameter values. Debugging would highlight that mistake. On order of processing, imagine is we had a
Function setTest(string) as String in the Bar class and had this code.
Dim bar1 as New Bar() Call demo.appendElem("1", bar1.setTest("One"))._ appendElem("2", bar1.setTest("Two"))
This code will call:
This may not be a surprise, but knowledge is always better than assumptions.
Although fluent coding is not the norm in LotusScript, it starts to become very attractive after some years coding in Java, particularly more modern Java. It makes code feel more modern and look cleaner, even if it’s not.