Peter Pressnel wrote a post about
class casting and polymorphism in LotusScript. Unfortunatelly, in LS you can only extend one class, not implement Interfaces (like in every other OO language). Interfaces lets you add functionality to a given class by implementing the Interface: a Person will also by "Compareable" by implementing that interface. Now every method which needs a "Compareable" argument can work with Persons.
This missing feature in LS basicly means that you have to build this functionality anew every time you need it: you need a person.compareToAnotherPerson(aPerson as Person) and also your sorted list (or whatever needs something compared) needs to know about that method. This means a lot of unnecessary programming and also it binds your code together and you will very soon get either a very big LS file or the famous "Illegal circular use" error. Not nice!
But we have help from the Adapter Pattern. Instead of adding the functionality by implementing an interface we ask for an adapter of the object which implements this interface [1].
The adapater pattern is usually also implemented via an interface: IAdaptable.getAdapter(classname) which either returns an object of that classname or nothing. As there is no Interfaces in LS this method is implemented in a basic class from which every other of my custume classes is derived: BasicObject. This class also implements "toString() (usefull as a message to logError()...) and such basic things.
The API contract for that method is very easy: either return an object of the specified class, which represents the original object or nothing. Classcasting an object now becomes:
dim managerĀ as Manager
set manager = person.getAdapter("MANAGER")
if manager is nothing then Error 1000, "Person is not a Manager!"
[...]
instead of
dim manager as Manager
if not (person isa ucase("Manager")) then error 1000, "Person is a not a manager!"
set manager = person
[...]
So the insert method of a sorted list, which expects a "Compareable" object (or nothing), would be something like this: list.insert(me.getAdapter("COMPAREABLE")).
This is the BaseObject.getAdapter implementation.
Class BaseObject
' [ public function toString() as String ... ]
'/**
' * Returns an Adapter of the current Object of the aked-for type or nothing
' * The basic implementation returns this object if the caller asked for the class
' * or a subclass of the class of the current object
' * Should be overwritten to do something more usefull!
' * @autor Jan Schulz
' */
Public Function getAdapter(nameOfObject As String) As BaseObject
If Ucase(nameOfObject) = Typename(Me) Then
Set getAdapter = Me
End If
If Me Isa Ucase(nameOfObject) Then
Set getAdapter = Me
End If
End Function
End Class
This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at nsftools.com.
An addition would be the implementation of an AdapterManager (see the article on eclipse.org): This would even solve the problem that you need to know the adapter class in the "adaptable" object (-> problems with "Illegal circular use"...). This would need a small AdapterFactory with one methods: getAdapter(object as BaseObject, classname as String). This factory would be implemented for each used combination and then an instance of such an object added to the AdapaterManager. Now you can query the AdapaterManager for an Adapater of your object: getAdapterManager().getAdapter(object, "SomeClassName"). In this method you would ask each adapterFactory for this combination and return if you get an object back (or nothing if no factory can return such an object). Now the object wouldn't need to know that it is adaptable to a "SomeClassName".
[1] This pattern is usually used in other OO languages to keep the API of an class small and clean: if you need your person compareable, the methods of the "Compareable" Interface would be part of the API contract for the "Person" class. This is usually not desireably, especially if you add serveral interfaces.