LotusScript Classes Deep Dive Part Four
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 NotesJsonNavigatorFluent
and 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 NotesJsonNavigatorFluent
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 NotesJsonNavigator.appendELement(passedElem).appendElement(passedElem2)
.
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:
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.
Caveat
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:
bar1.setTest("One")
demo.appendElement("1", "One")
bar1.setTest("Two")
demo.appendELement("2", "Two")
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.