OpenDOM

Friday, April 06, 2007

Interfaces & Abstract Classes with LotusScript


Interface and abstract classes define objects properties and methods, just as ordinary classes do. One generally creates as many objects as necessary from standard classes, while interface classes are solely intended to describe contracts that future objects will guarantee. Interfaces classes usually contain no code and can't be used to instantiate objects. In turn abstract classes prototype properties and methods. Abstract classes can't be instantiated either and require to be derived so that its subclasses benefit from their code. Compilers closely control interface compliant classes and usually prevent from interface or abstract classes instantiations.

Designers usually resort to interface or abstract classes to build loosely coupled application code that's more adaptable, more maintainable and ends up being more robust. Although such class constructs are missing from native LotusScript, those object oriented design concepts can be implemented in your LotusScript code with little effort.

As an exemple, consider designing a LotusScript.io.File class that holds the following subset of attributes and operations: canWrite( ), getAbsolutePath( ), length( ), list( ), renameTo( ). You may either develop a full LotusScript solution, either build an LS2J based solution, either resort to Windows Script Host COM object library or even call Windows/32 native functions. Defining iFile interface class sounds a valuable approach that exhibits properties and methods regardless of the implemented inner workings. While your calling code relies on a stable interface, your concrete implementations can vary at will or be changed without notice. Doing so you're building a loosely coupled Notes or Domino application.

Figure 1: StopWatches UML Class Diagram

OpenDOM open source project you can download from www.openntf.org contains numerous code samples benefitting from interfaces and abstract classes design constructs. Its LotusScript.lang.StopWatches library uses iTicker public interface in four different implementations accommodating LotusScript release 3 and 4+, Windows/16 and Windows/32 contexts. LotusScript.lang.Rn or LotusScript.windows.winnn packages, or namespaces, contain iTicker interface compliant concrete classes. StopWatches public class internal iTicker engine gets loaded dynamically according to the most appropriate situation.

You can prevent from objects instantiation, and make sure properties and methods get derived or overridden. But creating private LotusScript class constructors as in ..
Public Class iVehicle
Private Sub New
End Sub
End Class ' iVehicle
.. throws the following compiler exception :
<noteName>: <eventName>: <lineNumber>: Illegal PRIVATE declaration of: NEW

As LotusScript does not natively support interface classes or private constructors as above I adopted another approach. I prevent from interface or abstracts classes instantiation at runtime only throwing exception in class constructors, I force properties and methods to be derived throwing equivalent runtime exceptions as in :
%INCLUDE "LsErr.lss"
Public Class iVehicle '
Public Sub New ' This forbids interface instantiation, please derive me !
If Typename( Me ) = "IVEHICLE" Then Error ErrNotAnObject
End Sub ' New
Public Property Get Color As NotesColor
Error errPropGetNotDefined
End Property ' Color
Public Property Set Price As Currency
Error errPropSetNotDefined
End Property ' Price
Public Sub starts( )
Error ErrNotAMethod
End Sub ' starts( )
End Class ' iVehicle

Public Class aShuttle
Public Property Get isFree As Boolean '
Error errPropSetNotDefined
End Property ' isFree
End Class ' aShuttle

Once I established my interface, I can devise to write as many concrete classes implementing it and provide a factory class or routine serving context dependent objects :
Private Class Car As iVehicle
' Your code goes here ..
End Class ' Car As iVehicle

Private Class Truck As iVehicle
' Your code goes here ..
End Class ' Truck As iVehicle

Public Function createVehicle As iVehicle
If ( so And so ) Then
Set Me.createVehicle = New Car
Else
Set Me.createVehicle = New Truck
End If
End Function

OpenDOM LotusScript.lang.Factory class creates objects this way. It is inspired from « Performance Considerations for Domino Applications » IBM redbook whose « B-2 Dynamic Script Library Loading » worth reading appendix explains how to dynamically load LotusScript libraries in your object oriented programs. While IBM code performs objects dynamic instantiation, attentive reading reveals such is accomplished using variants that defeat compiler time type checks. Adding interface classes your code overcomes this obstacle and offers multiple advantages :
  • You're designing loosely coupled LotusScript while building application classes regardless of their internal technical alternatives you will freely accommodate,
  • Early bound objects allow type safety controls to occur at compile time which is considered a best practice,
  • LotusScript interpreter memory footprint increases progressively as library objects are gradually loaded. Although libraries are compiled prior to be loaded, this gets mostly unnoticed and is largely compensated by memory minimised consumption.
In order to achieve early binding of dynamic objects, Factory class makes intensive use of interface or abstract classes. As opposed to IBM NewObj original routine Factory class dynamically instantiates strongly typed objects, thus provides compile-time checks which enforces applications robustness and reliability.

Places of interest:

Labels: , , , , , , ,