IBM®
Skip to main content
    Country/region select      Terms of use
 
 
   
     Home      Products      Services & solutions      Support & downloads      My account     

developerWorks  >  Lotus  >  Forums & community  >  Best Practice Makes Perfect

Best Practice Makes Perfect

A collaboration with Domino developers about how to do it and how to get it right in Domino

I've been writing some LotusScript classes recently, and it occurred to me to write about this technique for improving performance --  hope it's not too obvious.

Frequently, classes have some properties you're not sure the caller will use. A lot of things, you would initialize in the constructor ("Sub New"), so that they will be available when needed. But others, which might be expensive to calculate, you'd like to delay calculating them until someone asks for them (the computer version of "slack"). But, if someone asks for them twice, you don't want to do the expensive operation twice.
The way I've been doing this, is to set up a "Property Get" for the item, and have a private member where the value is stored the first time it's calculated. For instance:

Const ERR_HM_NODATABASE = 18650
Const ERR_HM_NOPADDOCK = 18651
Const PADDOCK_VIEW_NAME = "PaddocksByTitle"

Class HorseManager
  m_db As NotesDatabase ' initially Nothing
  m_paddockView As NotesView ' ditto
  ...
  Public Property Get Database( )
     If m_db Is Nothing Then
        Set m_db = New NotesDatabase(m_server, m_filepath)
        If Not m_db.IsOpen Then
           Error ERR_HM_NODATABASE, "Error in HorseManager.Database: can't open '" & m_filepath & "' on '" & m_server & "'"
        End If
     End If
     Set Database = m_db
  End Property
   
  Public Property Get PaddockView() As NotesView
     If m_paddockView Is Nothing Then
        Set m_paddockView = Database.GetView(PADDOCK_VIEW_NAME)
        If m_paddockView Is Nothing Then
           Error ERR_HM_NOPADDOCK, "Error in HorseManager.PaddockView: view '" & PADDOCK_VIEW_NAME & "' not found."
        End If
     End If
     Set PaddockView = m_paddockView
  End Property
   
End Class

The property routine Database does the work of opening the database only once, the first time it's called. Thereafter, it notices the database is already stored in the private property m_db, and just returns the value from there. The PaddockView computed property works the same way, but since it uses the Database property internally, it might end up doing nothing (if the view has been requested before), or it might look up the view, or it might open the database and look up the view, if the database wasn't opened yet. Obviously we can continue the chain, e.g. we might have a GetPaddock(paddockName$) method that uses the PaddockView property to get its lookup view. And the on-demand properties don't have to be Public, either -- I've had occasion to use them internally in the class for values we don't need to expose to the world.

Notice how I throw errors if there's a problem computing the requested value. This is preferable to just returning a value that's not usable, because we explain the problem at the real point of failure. If PaddockView just returned Nothing in case of a nonexistent view, the caller wouldn't see that there was a problem until it tried to use the returned value, getting the much less informative error "Object variable not set." The caller of course has the option of trapping (or ignoring) the error and taking corrective action in either case, or test the return value of PaddockView before using it, but since this code provides a unique error number, it's easier to code something that addresses the specific issue or displays a useful message to the user. E.g. if PaddockView returns Nothing, you might want to know whether the problem is with the database or the view.

The price we pay for not having to calculate the values each time they are needed, is that we have to remember to clear the cached values should they become invalid. I generally define a private method to erase the cached data, which I call from various places in other methods when a relevant value changes (there might be more than one such routine, if there are a bunch of cached values that aren't all necessarily invalidated at the same time).

  Private Sub ClearCache
     Set m_db = Nothing
     Set m_paddockView = Nothing
  End Sub
   
  Public Property Set Filepath As String
     m_filepath = Filepath
     ClearCache ' any existing database and view objects are no longer valid
  End Property

It makes sense to use this technique if the operation that produces the data item is relatively expensive, and if it's possible that the value will not be required by the code that uses the class. In many cases, though, I've found that even aside from performance considerations, it results in cleaner looking code. You don't always have all the information you need to initialize everything in the constructor, and by using this technique, you know you don't have to worry that everything will be assigned in the right order for you to use it -- by using your own property routine internally, it's assigned at the time that you require it, if not before.
Of course, it's a little more expensive and more lines of code to call a property function as opposed to referring to a property member directly, but if they are read-only properties, you need the Property Get function anyway, and the performance cost is small compared to opening a database or view unnecessarily.

Andre Guirard | 19 September 2007 01:39:00 PM ET | Nokomis Beach Coffee, Minneapolis, MN | Comments (1)

Search this blog 

Disclaimer 

    About IBM Privacy Contact