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
Posted by Chris Toohey At 11:03:19 PM On 07/31/2008 | - Website - |
[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
Posted by Thomas Bahn At 06:39:40 AM On 08/01/2008 | - Website - |
Posted by Nathan T. Freeman At 08:02:25 AM On 08/01/2008 | - Website - |
-Devin.
Posted by Devin Olson At 08:16:25 AM On 08/01/2008 | - Website - |