After watching the great presentation from Stefan Hirsch (thanks Stefan!) here (<will post link as soon as it's uploaded to youtube>), there were some questions about why and when to use interfaces and abstract methods. I would like to add another sample about this:
Similar to Stefan's sample code, let's say we need to implement export to xml functionality in many objects of our application. For example we would like a certain window to be able to export its contents to an xml file, also one of our dbservers should do this, also a "CustomerDetails" class should be able to export the customer data to xml. Without interfaces, we could do this by simply adding a "ExportToXml" method in each class that needs to do this:
Code: Select all
CLASS MySpecialWIndow INHERIT DataWindow
METHOD ExportToXml(cFileName AS STRING) AS LOGIC
...
RETURN lSuccess
CLASS MySpecialDBServer INHERIT DBServer
METHOD ExportToXml(cFileName AS STRING) AS LOGIC
CLASS CustomerDrtails
EXPORT FirstName,LastName AS STRING
METHOD ExportToXml(cFileName AS STRING) AS LOGIC
Code: Select all
FUNCTION GeneralExportToXmlFunction(oObject AS USUAL) AS LOGIC
LOCAL cFilename AS STRING
LOCAL lResult AS LOGIC
cFileName := OpenDialogThatAsksForFilename()
IF .not. Empty(cFileName)
lResult := oObject:ExportToXml()
ELSE
lResult := FALSE
ENDIF
RETURN lResult
Code: Select all
GeneralExportToXmlFunction("sorry, mistake!")
But, if we had interfaces available in VO (as we do in X#), we could had defined one like that:
Code: Select all
INTERFACE IExportToXml
METHOD ExportToXml(cFileName AS STRING) AS LOGIC
END INTERFACE
Code: Select all
CLASS MySpecialWIndow INHERIT DataWindow IMPLEMENTS IExportToXml
METHOD ExportToXml(cFileName AS STRING) AS LOGIC
CLASS MySpecialDBServer INHERIT DBServer IMPLEMENTS IExportToXml
METHOD ExportToXml(cFileName AS STRING) AS LOGIC
CLASS CustomerDrtails IMPLEMENTS IExportToXml
METHOD ExportToXml(cFileName AS STRING) AS LOGIC
Code: Select all
FUNCTION GeneralExportToXmlFunction(oObject AS IExportToXml) AS LOGIC
LOCAL cFilename AS STRING
LOCAL lResult AS LOGIC
cFileName := OpenDialogThatAsksForFilename()
IF .not. Empty(cFileName)
lResult := oObject:ExportToXml()
ELSE
lResult := FALSE
ENDIF
RETURN lResult
Code: Select all
LOCAL oObject AS AnotherWindow
oObject := AnotherWindow{}
GeneralExportToXmlFunction(oObject)
It's like all cases where we have the choice to use strongly typing or not. Strong typing indeed does require to write a bit more code, on the other hand it results to faster execution and helps us find problems (with the help of the compiler) at compile time, instead of having users (or testers) find them at runtime, possibly after a long time.
Note that using a common base (ABSTRACT would be best) class can also be used often in cases like that. But not in our case, because it would require all our objects to inherit from that class. But by using an interface, we can have a class inheriting from DataWindow, another from DBServer, and another with no parent at all, all very different classes one to the other, but which all do one common things, they are all guaranteed to have a method named ExportToXml() that we can call with strongly typed, efficient and robust code.
Hope this further helps a bit to explain the beauty behind interfaces!