Dynamic Loading Script Libraries, Late Binding and a Flexible Parameter
Category Lotus Domino Lotus Notes Lotusscript
Beginning on page 243 of the greatly revered 'Performance Considerations for Domino Applications' there is a discussion of the concept of dynamically loading Lotusscript libraries that contain individual object oriented custom classes. I LOVE this technique. I have an application that has close to 150 classes and loading them all at once is a huge performance drain; this technique alleviates that problem.
But it does introduce a different problem.
On page 246, there is a note:
Note We did not need to pass parameters to the class constructor in our
application. However, we could have allowed parameters to be passed
into NewObj, which in turn would be passed into the constructor if
necessary. This would impose the restriction that the constructors of all
classes to be used with NewObj would need to accept the same
parameter.
To this I say 'Bollocks'! I really need to pass parameters to my classes...different parameters all the time...sometimes a string, sometimes a date, often an Notes object like a NotesUIDocument. Sometimes I even need to pass a whole slew of parameters to the class.
The code presented by the Redbook looks like this:
(error checking/handling removed from all code for brevity)
Function NewObj( className As String ) As Variant
If Not IsElement( factories( className ) ) Then
Dim script As String
script = |
Use "| & className & |"
Sub Initialize
Set newObj_factory = New | & className & |Factory
End Sub
|
Execute (script)
Set factories( className ) = newObj_factory
End If
Set NewObj = factories( className ).produce
End Function
Which is called like this:
Set obj = NewObj( "MyClass" )
My loader looks like this:
Function newObj(strClassName As String, parameter As Variant) As Variant
If Not Iselement(fact_objs(strClassName)) Then
Dim strLibName As String
strLibName = "class:" & strClassName
Dim strExec As String
strExec = |
Use "| & strLibName & |"
sub Initialize
set newObj_Factory = new | & strClassName & |Factory
End sub
|
Execute(strExec)
Set fact_objs(strClassName) = newObj_Factory
End If
Set newObj = fact_objs(strClassName).produce(parameter)
End Function
Which is called like this: (in this case I am passing a NotesUIDocument object as a parameter from a form's queryopen event)
Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)
On Error Goto ERROR_HANDLER
Set oDoc = newObj("Organization", Source)
Exit Sub
ERROR_HANDLER:
Call ErrorHandler.HandleError(MODULE_NAME, "", Null)
Exit Sub
End Sub
My factory class (which is what is used to instanciate the object, defined in the script library,) looks like this:
Public Class OrganizationFactory
Function produce(Source As NotesUIDocument) As Variant
Set produce = New Organization(Source)
End Function
End Class
And finally, the class itself (BaseForm is an 'intermediary' class that all of my form classes are derived from, it contains standard form processing code):
Private Class Organization As BaseForm
Sub New (Source As NotesUIDocument)
On Event PostOpen From Source Call EventPostOpen
On Event QueryModeChange From Source Call EventQueryModeChange
On Event PostModeChange From Source Call EventPostModeChange
On Event QuerySave From Source Call EventQuerySave
On Event PostSave From Source Call EventPostSave
End Sub
<snip>
bunch of event handling code
</snip>
End Class
This class is used to control the explicit processing I do against this particular form and it may or may not override the processing coded in the BaseForm class
All of my custom class script libraries follow a simple naming convention "class:" and then the class name, so for the code above my 'Organization' class resides in a library named 'class:Organization'.
What is cool about this loader/factory/class technique is that you get the benefits of dynamic loading, plus the ability to pass a parameter AND since you are passing around a reference to a Variant, the parameter can be anything known to the class (any native datatype, an object, a custom datatype (I use these for mutliple parameters.) All that matters is that the class define the required parameter either explicitly (for improved error checking) or expect a variant itself.
If you don't need a parameter, you still need to setup a 'dummy' parameter to statisfy the compiler, but you simply pass a null or nothing value and don't have your class do anything with the parameter.
Beginning on page 243 of the greatly revered 'Performance Considerations for Domino Applications' there is a discussion of the concept of dynamically loading Lotusscript libraries that contain individual object oriented custom classes. I LOVE this technique. I have an application that has close to 150 classes and loading them all at once is a huge performance drain; this technique alleviates that problem.
But it does introduce a different problem.
On page 246, there is a note:
Note We did not need to pass parameters to the class constructor in our
application. However, we could have allowed parameters to be passed
into NewObj, which in turn would be passed into the constructor if
necessary. This would impose the restriction that the constructors of all
classes to be used with NewObj would need to accept the same
parameter.
To this I say 'Bollocks'! I really need to pass parameters to my classes...different parameters all the time...sometimes a string, sometimes a date, often an Notes object like a NotesUIDocument. Sometimes I even need to pass a whole slew of parameters to the class.
The code presented by the Redbook looks like this:
(error checking/handling removed from all code for brevity)
Function NewObj( className As String ) As Variant
If Not IsElement( factories( className ) ) Then
Dim script As String
script = |
Use "| & className & |"
Sub Initialize
Set newObj_factory = New | & className & |Factory
End Sub
|
Execute (script)
Set factories( className ) = newObj_factory
End If
Set NewObj = factories( className ).produce
End Function
Which is called like this:
Set obj = NewObj( "MyClass" )
My loader looks like this:
Function newObj(strClassName As String, parameter As Variant) As Variant
If Not Iselement(fact_objs(strClassName)) Then
Dim strLibName As String
strLibName = "class:" & strClassName
Dim strExec As String
strExec = |
Use "| & strLibName & |"
sub Initialize
set newObj_Factory = new | & strClassName & |Factory
End sub
|
Execute(strExec)
Set fact_objs(strClassName) = newObj_Factory
End If
Set newObj = fact_objs(strClassName).produce(parameter)
End Function
Which is called like this: (in this case I am passing a NotesUIDocument object as a parameter from a form's queryopen event)
Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)
On Error Goto ERROR_HANDLER
Set oDoc = newObj("Organization", Source)
Exit Sub
ERROR_HANDLER:
Call ErrorHandler.HandleError(MODULE_NAME, "", Null)
Exit Sub
End Sub
My factory class (which is what is used to instanciate the object, defined in the script library,) looks like this:
Public Class OrganizationFactory
Function produce(Source As NotesUIDocument) As Variant
Set produce = New Organization(Source)
End Function
End Class
And finally, the class itself (BaseForm is an 'intermediary' class that all of my form classes are derived from, it contains standard form processing code):
Private Class Organization As BaseForm
Sub New (Source As NotesUIDocument)
On Event PostOpen From Source Call EventPostOpen
On Event QueryModeChange From Source Call EventQueryModeChange
On Event PostModeChange From Source Call EventPostModeChange
On Event QuerySave From Source Call EventQuerySave
On Event PostSave From Source Call EventPostSave
End Sub
<snip>
bunch of event handling code
</snip>
End Class
This class is used to control the explicit processing I do against this particular form and it may or may not override the processing coded in the BaseForm class
All of my custom class script libraries follow a simple naming convention "class:" and then the class name, so for the code above my 'Organization' class resides in a library named 'class:Organization'.
What is cool about this loader/factory/class technique is that you get the benefits of dynamic loading, plus the ability to pass a parameter AND since you are passing around a reference to a Variant, the parameter can be anything known to the class (any native datatype, an object, a custom datatype (I use these for mutliple parameters.) All that matters is that the class define the required parameter either explicitly (for improved error checking) or expect a variant itself.
If you don't need a parameter, you still need to setup a 'dummy' parameter to statisfy the compiler, but you simply pass a null or nothing value and don't have your class do anything with the parameter.






Comments
Posted by Charles Robinson at 12:11:10 PM on 06/29/2007 | - Website - |
Posted by Slawek Rogulski at 07:44:35 PM on 07/20/2007 | - Website - |
' I grab an (optional) default class object and get specific objects (with/out parms) this way..
Set myClass=NewObj( "pkg.Circle")
parameters) this way..
Set c1 = obj.getCircle1(point)
Set c2 = obj.getCircle2(radius, point)
' or resort to static class methods to build specific objects..
Set c1 = pkg.Circle.getCircle1(point)
Set c2 = pkg.Circle.getCircle2(radius, point)
I am convinced DOM objects should not be passed as parameters BUT «problem domain» objects instead, DOM objects are wrapped within objects encapsulating back-end and front-end abilities in class constructors:
Class Person
Private be As NotesSession
Public Sub New
Set be = New NotesSession
End Sub
End Class ' Person
Class UIPerson As Person
Private ui As NotesUIWorkSpace
Public Sub New
Set ui = New NotesUIWorkspace
End Sub
End Class ' UIPerson
thus registering to an event is a combination of intermingled UI and persistence routines initiated from agents, buttons, forms, views, etc..
aPerson.subscribes( aEvent )
thisPerson.subscribes( thisEvent )
Cordially
Posted by Alain H Romedenne at 04:29:49 AM on 07/24/2007 | - Website - |
Posted by fsdf at 05:51:00 AM on 08/23/2007 | - Website - |
Posted by aquila at 04:43:15 AM on 12/24/2007 | - Website - |