« IBM Press Release is backwards | Main| This is SICK! »

A quick reply to Tim (or Faking Abstract Methods in Custom Lotusscript Classes)


Tim's roadblock from last night with Lotusscript method inheritance is exactly the kind of thing that I read and think "well, there's gotta be a way to fix THAT."  So he and I chatted this morning, threw a couple of ideas against the wall, and got one to stick.  But we both immediately saw the problem: since there's no way to force derived classes to override a method (ie: declare a method abstract,) we could run the risk of some Lotusscript n00b (or anyone who happened not to read the documentation for the parent class, ie: yours truly) creating a nice nasty recursive call.

We knocked a few ideas around about how to prevent it, ultimately settling on using LSI_INFO() to get the call stack in order to determine we were running recursively.  But sooner or later, someone's going to complain that LSI_INFO isn't thread safe, and therefore write off the entire design.

Instead we tried a thread safe code lock, and it worked.  So going back to Tim's sample code, we have...

Private Sub Inviewedit(Source As Notesuiview, Requesttype As Integer, Colprogname As Variant, Columnvalue As Variant, Continue As Variant)
           If Not(Me.isRecursive(Fulltrim(Split(Lsi_info(14), Chr(10))))) Then
               Call Me.inviewEdit(Source, Requesttype, Colprogname, Columnvalue, Continue)
       End If
End Sub


...which we modified to...

Private Sub Inviewedit(Source As Notesuiview, Requesttype As Integer, Colprogname As Variant, Columnvalue As Variant, Continue As Variant)
        Dim mylock As Integer
        Dim status as Boolean
        myLock = Createlock("parentInviewedit")
        If Codelockcheck (myLock) > 0 Then        
                MessageBox "The person that coded this application sucks, because they didn't remember to override this event.  Find them, point at them, and laugh.", 48, "Bogofied"
        Else        'there is no spoon.... I mean lock
                If Codelock(myLock) Then
                       Call Me.inviewEdit(Source, Requesttype, Colprogname, Columnvalue, Continue)

                End If
                status = Codeunlock (myLock)
                status = Destroylock (myLock)
       End If
End Sub


The effect is that the method isn't permitted to call itself, even through a wrapper.  It's not truly an abstract method, because the compiler isn't going to ensure that you overrode it.  But the lock prevents a recursive call.  And that's really the important part.  Besides, with a message like that going in front of the user, it's not a mistake you're likely to make twice.

The createlock() could probably be made smarter.  My present thinking is that you could abstract the lock identifier to something like GetThreadInfo(LSI_THREAD_MODULE) + GetThreadInfo(LSI_THREAD_PROC) or something; anything to make the process more introspective so you don't have to remember to tweak the string that's provided.  I suppose you could just use some arbitrary constants.

I'm open to suggestions on way to more readily identify the current code being called.  There's very little wrong with the original LSI_INFO call except concerns over being thread safe,  As Tim says, that's not an issue in his UI-driven context.  But if you're going to use one coding model for all situations, then you probably want to protect methods you would normally want to be abstract in this same way.

By the way, Chris, with that shirt on in your Gravatar, Aquaman is the right fit.  But that's not a bad thing.  We're all just fish at heart.

Comments

1 - ... be thankful you can't see the green tights I'm wearing in that pic!

2 - To ensure that abstract methods are really overwritten in subclasses, we simply throw a custom error in the abstract method. This way, whenever a non-overwritten abstract method is called, the application stops with a message indicating the problem.

[code]
Public Class AbstractFactory


Public Sub New()

If Typename(Me) = "ABSTRACTFACTORY" Then ' circumvent instantiation of abstract class
Error 1, Replace(GetProperty(LIBRARY & ".errmsg.abstract.class"), "<%CLASSNAME%>", "AbstractFactory")
End If

If Not IsDebugMode Then On Error Goto errorHandler
' do what you want to do here...
Exit Sub

errorHandler:
If HandleError() Then Resume Next
End
End Sub ' AbstractFactory.New


Public Function CreateInstance() As Variant
Error ERRNO_PROCEDURE_NOT_IMPLEMENTED, GetProperty(LIBRARY & ".errmsg.procedure.not.implemented")
End Function ' AbstractFactory.CreateInstance


Public Function CreateInstanceWithArgs(args As Variant) As Variant
Error ERRNO_PROCEDURE_NOT_IMPLEMENTED, GetProperty(LIBRARY & ".errmsg.procedure.not.implemented")
End Function ' AbstractFactory.CreateInstanceWithArgs


Public Function CreateInstanceOf(className As String) As Variant
Error ERRNO_PROCEDURE_NOT_IMPLEMENTED, GetProperty(LIBRARY & ".errmsg.procedure.not.implemented")
End Function ' AbstractFactory.CreateInstanceOf


Public Function CreateInstanceOfWithArgs(className As String, args As Variant) As Variant
Error ERRNO_PROCEDURE_NOT_IMPLEMENTED, GetProperty(LIBRARY & ".errmsg.procedure.not.implemented")
End Function ' AbstractFactory.CreateInstanceOfWithArgs

End Class ' AbstractFactory
[/code]

To prevent the instantiation of an abstract class, we check in the constructor the class of the current object. It it is still the name of the abstract class, we throw an error.

Thomas

3 - Ah, but Thomas, if you read through the whole chain, that isn't what we want to do. The abstract method can't throw an error -- it must simply call the child method that inherits it. You have to read Tim's posts from the beginning! Emoticon

4 - I am glad you decided to go with something other than LSI_INFO().

-Devin.

Post A Comment

:-D:-o:-p:-x:-(:-):-\:angry::cool::cry::emb::grin::huh::laugh::lips::rolleyes:;-)

11 Aug 

Hire Me 

Lotus-911-Logo.jpg

Search 

Disclaimer 

Welcome to Escape Velocity!

Opinions expressed here by Nathan T. Freeman are not necessarily those of his employer. However, there's a decent chance they are, so check with them if you really want to know.

But really... do you need that kind of validation? Are the opinions expressed here in doubt?

MiscLinks