Mr. Cluey
: Special Topics : Object Based SubsystemsAn object based subsystem is a collection of related functions that are implemented as the methods of an object, rather than as straight function calls.
There are a lot of subsystems that are used, and reused, regularly in QAP.
All identifiers (function names, method names, variables, etc...) are defined within a particular scope. The scope of a function is global. This means that you can only have one Print function, for instance.
The scope of a method is the window in which it is defined. You can't have two print functions, but you can have two windows, A and B, which different print methods.
The big gain is that you can then switch between subsystems on the fly - getting different behaviors without having to change your source code.
The general idea is that we will create two objects (windows) which perform a particular task. In our scripts, we always perform that task using a master window. During startup, we choose which particular system we want to use, and assign the master window to point to that system.
window CustomWin A
{
Report ( string sMsg ) { print ( sMsg ) ; }
}
window CustomWin B
{
Report ( string sMsg ) { print ( Upper(sMsg)) ; }
}
window theReporter = @(THE_REPORTER) ;
main ()
{
theReporter.Report( "hey, I'm on Web TV!" ) ;
}
In this sample, THE_REPORTER is a compiler constant. If the user assigns "A" to THE_REPORTER, the message hey, I'm on Web TV! appears in the results file. If "B" is in use, then HEY, I'M ON WEB TV! is there instead.
Useful? No, this is just an example. But you might want to write to the results file in some cases and to a text file in others. Maybe Joe wants things emailed to him. Perhaps you want to categorize the messages being written.
The key idea here is flexibility - by working with objects, you can change the way a test behaves without having to change any of the code.
Not necessarily. We can save a lot of work down the road by creating a class for reporters, and derriving the individual reporters from the main class, adding code only to make each reporter more specialized.
Method scope works from the inside out. When you write Foo.Bar(), QAP will prefer to do the Bar() that is defined inside of Foo's declaration. If there isn't one there, it will use the Bar() defined in Foo's class. It will continue searching up the inheritance chain, and if all else fails, it will look for the global function Bar().
Ah, but once you create an object subsystem, you can create wrapper functions around it, so that your coders can't tell how you have things implemented.
Here is a more complete example of reporter.inc, the library which encapsulates the example above and hides the implementation details from the users...
//reporter.inc - an API wrapper around the reporter example
// wrapper functions
// ------- ---------
// the generic form is
// datatype METHOD( param list ) { return theReporter.METHOD( param list ) ; }
Report( string sMsg ) { theReporter.Report( sMsg ) ; }
// this function here ensure that we don't have a problem when
// THE_REPORTER is missing from the list of compiler constants
window GetReporter ()
{
string ReporterName = "DefaultReporter" ;
do
{
// the funky reference operator allows this code to
// compile even on machines where THE_REPORTER is missing
// from the list of Compiler constants.
ReporterName = @("THE_REPORTER") ;
}
except
{
; //Nothing to do here - ReporterName is already set to the default value.
}
return @(ReporterName) ;
}
// Here he is - our favorite primetime anchor
window theReporter = GetReporter () ;
// The definition of a GenericReporter
winclass CReporter
{
Report ( string sMsg ) { Print ( sMsg ) ; }
}
// The pool of available reporters
//Default Reporter - no need to declare anything. He inherits the generic implementation
window CReporter DefaultReporter {;}
// ExcitedReporter overrides the Report method.
window CReporter ExcitedReporter
{
Report ( string sMsg ) { Print ( Upper(sMsg) ) ; }
}
Well, if you are using Silk, you are almost certainly doing something like this already. To make it easy for users to write tests that work in both the Net$cape and Micro$oft browsers, Segue created the Browser window. The Browser window is really nothing more than a wrapper around the actual declarations for those two browsers. The only piece that is missing is the wrapper functions around the browser window itself.
There are two that leap immediately to mind.
window CustomWin A
{
Print ( string sMsg )
{
// Calls the Print() function
::Print(sMsg) ;
}
}
window CustomWin B
{
Print ( string sMsg )
{
// Calls B.Print again - eventually blows the stack
Print( sMsg ) ;
}
}
main ()
{
A.Print( "Hi" ) ; // Hi
B.Print( "Hi" ) ; // *** Error: Stack overflow
}
| Mr. Cluey : Special Topics : Object Based Subsystems |