OpenDOM

Wednesday, March 21, 2007

Static Properties & Methods in LotusScript Classes


In Java and JavaScript languages, static properties and static methods are shared by all instances of a given class. Non static members relate to objects instantiated from a class and are relevant to a single instance of that class thus File objects share pathSeparator operating system dependent static property, while getAbsolutePath( ), canWrite( ) properties differ from a File object to another as the delete( ) method yields separate results for every object. Utilizing static members does not require to instantiate objects e.g. java.io.File class pathSeparator property can be referenced typing File.pathSeparator or java.io.File.pathSeparator.

Creating class level properties or methods in LotusScript may be useful. Consider a Platform class with properties such as Newline and supportsCOM. Although this class exhibits different information depending on the operating system, Newline and supportsCOM results are identical across all instances of the Platform class for a given Notes client or Domino server. Imagine a LotusScript.math package holding a Trigo utility class with sin(), cos() methods and a PI static property whose precisions are greater than that of native LotusScript Pi constant and functions.

Although static members are not available in LotusScript to the extent Java and Javascript languages provide them, you can implement similar constructs with identical coding syntax. As an exemple I shall use LotusScript.lang.Interpreter class and its Release property, available from OpenDOM open source project at www.openntf.org site to illustrate my point.

Private Release As String
Release = lsi_info( 6 )

While these two lines are the simplest way to compute a given LotusScript interpreter release, I like to protect my code from any sins including my own and I tend to adopt the following defensive coding technique :

Static Private Property Get INTERPRETER_RELEASE As String
Static this As String
If this = "" Then this = Lsi_info( 6 )
INTERPRETER_RELEASE = this
End Property ' LotusScript.lang.Interpreter.RELEASE property

LotusScript simplified Interpreter public class spells as follows :

Public Class Interpreter '
Property Get RELEASE As String '
Me.RELEASE = INTERPRETER_RELEASE
End Property ' LotusScript.lang.Interpreter.RELEASE
End Class ' LotusScript.lang.Interpreter Class

Note every objects instantiated from the Interpreter class share a common RELEASE property that can be qualified static as in Java and JavaScript. The Interpreter.RELEASE syntax can be implemented in "LotusScript.lang" script library as :

Static Public Property Get Interpreter As Interpreter
Static this As Interpreter
If this Is Nothing Then Set this = New Interpreter
Set Interpreter = this
End Property ' LotusScript.lang.Interpreter class

Such ability can be extended to LotusScript.lang.Interpreter.RELEASE fully qualified class name coding two extra classes and two properties as in :

Public Class lang '
Public Property Get Interpreter '
Set Me.Interpreter = LANG_INTERPRETER
End Property ' LotusScript.lang.Interpreter
End Class ' LotusScript.lang.* Package
Public Class LotusScript '
Public Property Get lang As lang '
Set Me.lang = LOTUSSCRIPT_LANG
End Property ' LotusScript.lang.* Package
Public io As Variant ' As io__ '
Public net As Variant ' As net__ '
End Class ' LotusScript.* Package

Static Private Property Get LOTUSSCRIPT_LANG As lang
Static this As lang
If this Is Nothing Then Set this = New lang
Set LOTUSSCRIPT_LANG = this
End Property ' LotusScript.lang.* package
Static Public Property Get LotusScript As LotusScript
Static this As LotusScript
If this Is Nothing Then
Set this = New LotusScript
End If
Set LotusScript = this
End Property ' LotusScript.* package

Any agents, forms or views now require few lines of code to fully reuse "LotusScript.lang" package library :

Use "LotusScript.lang"
Msgbox LotusScript.lang.Interpreter.RELEASE,, _
"LotusScript.lang.Interpreter.RELEASE"
' OR
With LotusScript.lang
Msgbox Interpreter.RELEASE,, _
"Interpreter.RELEASE"
End With

Labels: , , , , ,

8 Comments:

  • Alain, I can see WHAT you're doing here, but I'm trying to figure out WHY. I've been a Lotuscript OO developer for many years now, but I am extremely informal about it. I know it's just an example, but my reading here is that it's an awful lot of code just to get lsi_info(6)!

    Can you give an example of a Static method that would truly return the same result for all instances of a class, where that method is something distinctive, like, say, a NotesDatabase handle?

    And is it possible (and I know this is unlikely) that this is applicable across frames in any way? If I have myClass instantiated via a script library through a form in frame 1, and load the same form in frame 2 -- there's no way to have a static method across instances in those two spaces is there? Because if you can describe a way to do that, you will have made my year. :-)

    And yes, I'll be downloading OpenDOM and taking a close look at it this afternoon.

    By Anonymous Nathan T. Freeman, at 4:33 AM  

  • Nathan,

    (1) Regarding static members I invite you to look at TICKER_ENGINE static property in LotusScript.lang.StopWatches as well as API_LIBRARY, API_DATABASE in LotusScript.notes.NotesDatabase package. I welcome any question that may raise..

    (2) You're right saying that such code sharing is not crossing forms or views borders. LotusScript interpreter is loading libraries in separate address spaces for every forms or views instances. Staticity only benefit to these isolated widgets which is far reaching the behaviour of a JVM. Previous debates within Domino blogosphere around singleton design pattern stressed this point.

    Cordially
    Alain

    By Blogger Alain H Romedenne, at 8:22 AM  

  • Nathan - a good example would be if you instantiate a class and one of its elements might be a configuratin class, containing all configuration values.

    Multiple instances of your target class could all share the same (usually read-only) version of configuration, for example. This gets significant if the time to get that configuration information gets large - you'd certainly want to "cache" stuff in this respect.

    In our product - we dont use these explicit "static" style declarations - we tend to have a "factory" class which then, when asked to create other classes, pre-loads a class reference with an already existing "config" class instance (for instance) - so hence getting the *effect* of being static.

    I'd *love* to be able to somehow gain access to other instances of lotusscript in terms of sharing this type of setup information - loading our factory class certainly isnt trivial and we dont like to keep our users hanging around too much.

    So if *that* issue could be addressed - bonus. Ironically, in the more loosely bound (and often criticised) javascript environment, at least it starts from a common root point and you can gain access to "other" frames (in the context of a web window)...

    Good stuff Alain - as always!

    ---* Bill

    By Blogger wild bill, at 5:08 AM  

  • A typical use-case of class attributes aka static properties is as storage of administrative information about the objects of this class.

    Say you want to count all instances created of a certain class. Using a imaginary static attribute you could simply write

    Public Class Page

    Static count As Integer

    Static Function GetCount() As Integer
    GetCount = count
    End Function

    Public Sub New()
    count = count + 1
    End Sub

    End Class

    sample usage:
    Print Page.GetCount ' -> 0
    Dim firstPage As Page
    Set firstPage = New Page()
    Print Page.GetCount ' -> 1


    Another use-case is the Singleton pattern: This design pattern assures, that exactly one instance of a class exists. An implementation would look like this:

    Public Class Configuration

    Static Private instance As Configuration

    Static Public GetInstance() As Configuration
    If instance Is Nothing Then
    Set instance = New Configuration()
    End If
    Set GetInstance = instance
    End Function

    Private Sub New()
    End Sub

    End Class


    If you do not create only one object, but more, and put them into an array or list, you could implement an ObjectPool class.


    But since there are no class attributes, we have to use global variables or FactoryClasses (not ideal, since direct instantiation cannot be prohibited this way).

    I like to think of script libraries as "super classes":
    I define a class in its declaration section and use the library's variables, subs and functions as "static" class attributes and methods.

    Thomas

    By Anonymous Thomas Bahn, at 7:29 AM  

  • Interesting but...
    I'am using Notes V6.5, Static is not allowed between Class and End Class (errot message = Illegal Static on and the variable name)
    Is it a new feature of V7

    By Blogger Thierry from paris, at 9:06 AM  

  • @Thomas

    Configuration or singleton objects typically map static properties & methods problem's domain.

    I keep away from global variables to avoid excessive exposure, Static properties (at library level) permit such. Package name syntax as in LotusScript.lang.String.replace() or LotusScript.notes.NotesDocument avoid public method or property name collision which justifies using this notation.

    LotusScript interface classes prevent from objects instantiation.

    @Thierry

    Observe I'm tweaking LotusScript since I define static properties and methods at library level
    and using CAPITALS to denote such within class statements.

    Defining properties at library level as been available from Notes & Domino release 4.

    Cordially
    Alain

    By Blogger Alain H Romedenne, at 3:04 AM  

  • Alain, recently I developed my new standard for library naming which as I hoped help me to have the library well organized. I ended up with something like com.mycompany.LS.GNRL.LIB.BG.utilities
    Such name tells me what to expect from the library, where it can be used, if it is general library or a special library developed for a certain project etc. From the begging everything seemed to work perfectly but then I found out (it cost me many hours!) that such a name cannot be used in background agents especially if the libraries are organized in hierarchy (or chained if you want). It seems that there is a strange limit imposed on number of characters in library name or on the number of dots in the name (still not sure) and if it isn't obeyed then library is not loaded. I would like to point out that I observe such behaviour only in background agents (in UI agents the long library name is accepted).
    Then I gave having everything coded in the name and now it works except that it does not look beautifully tidy and well organized as it did before.
    Now the question: did you come across the similar misbehaviour? I notice that you are using the similar library naming in your project so that's why I am asking.

    By Anonymous Mirek Navratil, at 7:55 AM  

  • Mirek - Thank, You!

    There is something fishy going on that is dependent on the length of the Scriptlibrary name.

    I just spend a couple of hours trying to figure out why a class initialized in a Scriptlibrary was not visible when used in one Agent but it was in another.

    One of the most obvious differences between the agents where that one was triggered from the Action Meny and the other was set to run from the Agents list (triggered by a view action formula). They where both running in the UI.

    When I cut down on the length of the name of the Scriptlibraries involved everything started to work as expected.

    Thank you very much!

    Peter Närlund, DomainPatrol

    By Blogger Peter Närlund, at 9:36 PM  

Post a Comment

Links to this post:

Create a Link

<< Home