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: , , , , , , ,

3 Comments:

  • I strongly agree with you: Interfaces and abstract classes are missing from LotusScript. We use the Error-statement in abstract methods, too, but the idea using
    If Typename( Me ) = "IVEHICLE" Then...
    in interfaces is simply ingenious :-)

    But there is another LotusScript restriction hinders real-world usage of interfaces: You can only inherit from one superclass. Therefore the following code does not compile:

    Class AObject
    Public a As Integer
    End Class

    Class BInterface
    Public Function GetB() As String
    End Class

    Class CObject As AObject, BInterface
    ' ...
    End Class

    But without the ability to inherit from a superclass and one to many interfaces, their usage is restricted to some simple use cases.


    I really like Notes, but knowing Java, there are so many limitations... :-(

    Thomas

    By Anonymous Thomas Bahn, at 1:02 PM  

  • I am curious as to how this and other fully fledged OO methods implemented in your LS code have enabled you to scale the complexity of your applications. Or have the improvements translated into more maintainable and reliable code.

    As for the benefits of your method over the dynamic linking courtesy of Variants it only holds for the case of polymorphic methods. Were you to add new methods in the subclasses (your concrete implementations) they would not be accessible via the interface.

    There are many more places where the LS implementations of the missing OO features are not quite there and so I wonder what is the overall benefit.

    Like Thomas I have to say that once you have programmed in a more expressive language like Java (never mind smalltalk, Ruby, etc) you feel very constrained (and frustrated) by the limitations of LS.

    Slawek

    By Blogger Slawek, at 1:36 AM  

  • Thomas, Slawek,

    I agree with both of you as far as LotusScript limits can be a source of frustration compared to other languages' expressivity. Although weakly typing benefits vs strongly typing remains debated on the Internet (Ruby on Rails, JavaScript vs Java .Net and the like)

    However, overcoming LotusScript constraints is day-to-day Domino designers' challenge, accompanied with satisfaction and sometimes frustrations. Be as they may, that is applied to simple use cases or polymorphic situations, and combined with dynamic library loading, coding interfaces in LotusScript is worth exploring.

    Cordially
    Alain

    By Blogger Alain H Romedenne, at 5:47 AM  

Post a Comment

Links to this post:

Create a Link

<< Home