Subject: RE: OO vs Script Libraries
I use classes a fair bit in LotusScript. I’m currently working on a tool to take files and convert them into file resources. This is implemented as a class, but there are different file resources you might want to create, and they have different code required to create them. Not a lot different, but some different. There’s a lot of code they have in common – for instance, the code to take a NotesStream and base-64 encode it is the same for all types of file resources. But an image resource needs to be able to specify properties like Across and Down which don’t apply to other types of resources. A Stylesheet resource has to let you specify a character set, which is not relevant for an image. An image resource can use a descriptive type of DXL, while other file resources only support the “note style” of DXL. So I created a hierarchy of classes.
GenericFileResourceImporter
NoteStyleFileResourceImporter
StyleSheetResourceImporter
FileResourceImporter
ImageResourceImporter
So to import an image, I can write:
Dim iri As New ImageResourceImporter(db)
iri.Across = 3
iri.Down = 1
iri.ColorizeGrays = True
iri.ImportFile filepath
Each of these classes has an ImportFile method, but only the ImageResourceImporter has the Across, Down and ColorizeGrays properties. Whichever one of these classes I Dim a variable of, I can call ImportFile, and they all use the same code.
Each class has the opportunity to override any part of its inherited functionality, and this overriding can occur in the middle of something which is not overridden. For instance, as part of the ImportFile method, we need a template string of DXL with flag sequences in it that we’re going to substitute in the specific values for whatever we’re importing. So the ImportFile function uses the TemplateDXL property, which returns the DXL to be used for plugging variable values into. Naturally, this template is different for different types of file resources.
If we implemented this procedurally, we could have the ImportFile function accept an argument that contains either the template string (requiring the caller to know it) or an argument that would be an input to a case statement that selects the right template string based on the type of import being done. In either case, the caller is providing extra information every time they call a function, and the code becomes more complex and more apt to be screwed up by the caller providing the wrong value. ImportFile would also have to accept arguments detailing the properties specific to the import (Across and Down, PublicAccess, Language), and most of these would only apply to certain types of imports. So you get something like this:
Call ImportFileResource(RESTYPE_STYLESHEET, strFilepath, true, false, true, 3, “en”, true, true)
Compare this with the previous example for readability.
Now think about what happens if you want to add new functionality. Say you want to write an AppletResourceImporter and make use of the same code. If you do it procedurally, you need to go into the various procedures and add cases to handle the new import type. The experienced developer recoils in fear. “You want me to change working code that’s used all over the place?” she says, her voice quivering. “Are you planning to retest all the applications that use this code?”
If you’ve used OO for the original design, you can reply, “Never fear! We’re not going to touch your working code.” You can just write a new script library:
Option Declare
Option Public
Use “Class NoteStyleFileResourceImporter”
Class AppletResourceImporter As NoteStyleFileResourceImporter
Public Property TemplateDXL As String
TemplateDXL = ...
End Property
… and so on
End Class
We can take advantage of the pre-existing code without changing it, just extending it by replacing the hooks in the base class with the little bits of code that are different.
Of course this requires careful design of the base classes to make sure the right bits are encapsulated in separate routines to be available for overriding. But that’s why good OO developers make the bug bucks.