现在的位置: 首页 > 综合 > 正文

Class Hierarchies in LotusScript – let’s loosen things up!

2011年05月27日 ⁄ 综合 ⁄ 共 9008字 ⁄ 字号 评论关闭

Coming back to Object-Oriented programming in LotusScript after a lengthy spell programming in only JavaScript was a bit of a shock: LotusScript isso restricted!  Everything has to be declared and set up in rigid class hierarchies that can't be rejigged on the fly like you can in JavaScript (which has no real classes, of course).  And yet, with the judicious use of LotusScript's Variant type, it's possible to free yourself from some of these more obvious restrictions.

Problem - Class Hierarchies
One restriction that's always bugged me about LotusScript OO, is that the class libraries are a one-way door.  Class Library A can access the classes in Class Library B, or Class Library B can access teh classes in Class Library A.  But they can't both access each other's classes.  To do so would set up an infinite loop of Use statements, which the Domino Designer Compiler does not allow.

Here's an example of this problem.  I set up two classes in two separate LotusScript class libraries:  The Class CarClass is in the CarClasses library and the Class RoadClass is in the RoadClasses library.  Here's the code for the two classes:

' in CarClasses LotusScript libary
Public Class CarClass
              Private m_Name As String
              Private m_TopSpeedKMH As Integer
              Private m_Weight As String

              Sub new(n, k, w)
                              Me.m_Name = n
                              Me.m_TopSpeedKMH = k
                              Me.m_Weight = w

              End Sub

              Public Property Get theName As String
                              theName = Me.m_Name
              End Property

              Public Property Get topSpeedKMH As Integer
                              topSpeedKMH = Me.m_TopSpeedKMH
              End Property

              Public Property Get Weight As String
                              Weight = Me.m_Weight
              End Property

End Class

' in RoadClasses LotusScript library
Public Class RoadClass
              Private m_Name As String
              Private m_Length As String
              Private m_ChargeType As String

              Sub new(n, l, c)
                              Me.m_Name = n
                              Me.m_Length = l
                              Me.ChargeType = c

              End Sub

              Public Property Get theName As String
                              theName = Me.m_Name
              End Property

              Public Property Get theLength As String
                              theLength = Me.m_Length
              End Property

              Public Property Get chargeType As String
                              ChargeType = Me.m_ChargeType
              End Property

              Public Property Set chargeType As String
                              If chargeType <> "" Then
                                              Me.m_ChargeType = "Freeway"
                              Else
                                              chargeType = chargeType
                              End If
              End Property

End Class

And here's a test agent that creates object of both classes.  Obviously, that code must (and does) include Use statements that name the two libraries.

' Options section
Option Public
Option Declare
Use "RoadClasses"
Use "CarClasses"

Sub Initialize
              Dim myCarObj As New CarClass("Honda Jazz", 120, 100)
              Dim myRoadObj As New RoadClass("M4", 500, "Tollway")

              Messagebox "objects defined!"

End Sub

And here's how the objects look in the LotusScript Debugger when I've run my test agent:

So far, so easy.  Now I want to extend CarClass to include some info about the roads on which it can travel.  I do this by creating an array of RoadClass objects as a member of CarClass, remembering to add the line Use "RoadClasses" to the Options section of the CarClasses library, so that CarClass knows about RoadClass.

' In Options
Use "RoadClasses"

Public Class CarClass
              Private m_Name As String
              Private m_TopSpeedKMH As Integer
              Private m_Weight As String
              Private m_PermittedRoads() As RoadClass

              Sub new(n, k, w)
                              Me.m_Name = n
                              Me.m_TopSpeedKMH = k
                              Me.m_Weight = w
                              Redim Me.m_PermittedRoads(0)
              End Sub

              Public Function addRoad(newRoad As RoadClass)
                              Dim arraySize As Integer
                              If Me.m_PermittedRoads(0) Is Nothing Then
                                              Set Me.m_PermittedRoads(0) = newRoad
                              Else
                                              arraySize = Ubound(Me.m_PermittedRoads)
                                              Redim Preserve Me.m_PermittedRoads(arraySize + 1)
                                              Set Me.m_PermittedRoads(arraySize + 1) = newRoad

                              End If
              End Function

              Public Property Get theName As String
                              theName = Me.m_Name
              End Property

              Public Property Get topSpeedKMH As Integer
                              topSpeedKMH = Me.m_TopSpeedKMH
              End Property

              Public Property Get Weight As String
                              Weight = Me.m_Weight
              End Property

End Class

I then modify the test agent to pass some RoadClass objects into my CarClass object.  Here's how the code and its results look in the Domino Debugger:

You can see how the m_PermittedRoads property of the myCarObj contains an array of RoadClass objects, as we intended.  But what happens if we also want to do the reverse of this; i.e. I want my RoadClass object to have an array of CarClass objects, which are the types of car that are permitted to drive on my road?

Here's how I might try to set that up in CarClass class.  You can see that I've added a Use statement to pull in the CarClasses library.  I've then setup a class variable that is an array of CarClass objects.

' In Options
Use "CarClasses"

Public Class RoadClass
              Private m_Name As String
              Private m_Length As String
              Private m_ChargeType As String
              Private m_CarTypes() As CarClass
             ....

And here is where it all falls down.  The Domino Designer won't save this code; it will throw the error "RoadClasses (Options): 3: Error loading USE or USELSX module: CarClasses".

Why won't it load the "CarClasses"?  It's because the "CarClasses" library is already loading the "RoadClasses" library via its Use statement.  And as I said earlier, two LotusScript libraries cannot "load" each other.  That would put us you an infinite loop where CarClasses uses RoadClasses, which uses CarClasses, which uses RoadClasses .... and round, and round we go. Not suprising then that the Designer throws a wobbler.

Solution
One possible solution is to put both classes in the same library, but this leads to an inflexibility that might end up causing more problems than it solves.

My answer is to instantiate my class objects as type Variant, rather than as their actual class-types.  The Designer does not do compile-time type checking for Variants, so we don't then need to have each library contain a Use statement that tries to load up the other.  Only the calling code - our test agent in this case - needs to load up the two libraries.  This allows us to eat our cake and still have it.

The code below shows the changes: the m_PermittedRoads class variable and the newRoad parameter of the addRoad function are both now defined as Variants.  This means that we no longer have to include a Use "RoadClasses" line in the Options section of the library; we merely need have both libraries loaded in the calling agent, as we did previously.  The RoadClass objects are declared and instantiated in the calling test agent, and then passed into CarClass's addRoad method as a Variant.

I've also added a PermittedRoads Get Property so that we can access the variable later on and test what it is and what it can do.  Here's the modified CarClass library.  Note that the problematic Use "RoadClass" line is gone as we no longer need it.

Public Class CarClass
              Private m_Name As String
              Private m_TopSpeedKMH As Integer
              Private m_Weight As String
              Private m_PermittedRoads() As Variant

              Sub new(n, k, w)
                              Me.m_Name = n
                              Me.m_TopSpeedKMH = k
                              Me.m_Weight = w
                              Redim Me.m_PermittedRoads(0)
              End Sub

              Public Function addRoad(newRoad As Variant)
                              Dim arraySize As Integer
                              If Me.m_PermittedRoads(0) Is Nothing Then
                                              Set Me.m_PermittedRoads(0) = newRoad
                              Else
                                              arraySize = Ubound(Me.m_PermittedRoads
)
                                              Redim Preserve Me.m_PermittedRoads(
arraySize + 1)
                                              Set Me.m_PermittedRoads(arraySize + 1)
= newRoad
                              End If
              End Function

              Public Property Get theName As String
                              theName = Me.m_Name
              End Property

              Public Property Get topSpeedKMH As Integer
                              topSpeedKMH = Me.m_TopSpeedKMH
              End Property

              Public Property Get Weight As String
                              Weight = Me.m_Weight
              End Property

             Public Property Get PermittedRoads As Variant
                              PermittedRoads = Me.m_PermittedRoads
             End Property

End Class

Here's the modified calling agent as it appears on the LotusScript Debugger. I've called the new PermittedRoads property of myCarObj tor return the first of the RoadClass objects that I passed into it on the agent's two previous lines.  Although this object is returned as type Variant (as displayed in the Degugger), I can still access it's theName property, which proves that it's really a RoadClass object.

抱歉!评论已关闭.