Object Oriented Programming |
![]() ![]() ![]() |
QMBasic includes support for object orientated programming. Users familiar with other object oriented languages will find that QM offers many of the same concepts but, because they are integrated into an existing programming environment, there may be some significant differences in usage.
What is an Object?
An object is a combination of data and program operations that can be applied to it. An object is defined by a class module, a QMBasic program that is introduced by the CLASS statement and contains the definitions of persistent data items and public subroutine and functions. An object is a run time instance of the class, instantiated by use of the OBJECT() function OBJ = OBJECT("MYCLASS") where "MYCLASS" is the catalogue name of the class module. The OBJ variable becomes a reference to an instance of the class.
A second use of the OBJECT() function with the same catalogue name will create a second instance of the object. On the other hand, copying the object variable creates a second reference to the same instance.
In other program types, data is stored either in local variables that are discarded on return from the program, or in common blocks that persist and may be shared by many programs. A class module has the additional concept of persistent data that is related to the particular instance of the object and is preserved across repeated entry to the object. If an object is instantiated more than once, each instantiation has its own version of the persistent data.
Persistent data is defined using the PRIVATE or PUBLIC statements: PRIVATE A, B(5) PUBLIC C, D(2,3) These statements must appear at the start of the class module, before any executable program statements. Data items defined as private are only accessible by program statements within the class module. Data items defined as public can be accessed from outside of the class module (subject to rules set out below). Private and public data items are frequently used to store what other object oriented programming environments would term property values.
PRIVATE and PUBLIC variables are set to unassigned when the object is instantiated.
Public Functions and Subroutines
Another important difference between class modules and other program types is that a class module usually has multiple entry points, each corresponding to a public function or public subroutine. Indeed, simply calling the class module by its catalogue name will generate a run time error.
Just as with conventional QMBasic functions and subroutines, a public function must return a value to its caller whereas a public subroutine does not (though it can do so by updating its arguments).
A public function is defined by a group of statements such as PUBLIC FUNCTION XX(A,B,C) ...processing... RETURN Z END where XX is the function name, A, B and C are the arguments (optional), and Z is the value to be returned to the caller.
A public subroutine is defined by a group of statements such as PUBLIC SUBROUTINE XX(A,B,C) ...processing... RETURN END where XX is the function name and A, B and C are the arguments (optional)
The number of arguments in a public function or subroutine is normally limited to 32 but this can be increased using the MAX.ARGS option of the CLASS statement.
Both styles of public routine allow use of the VAR.ARGS qualifier after the argument list to indicate that it is of variable length. Argument variables for which the caller has provided no value will be unassigned. The ARG.COUNT() function can be used to find the actual number of arguments passed. A special syntax of three periods (...) used as the final argument specifies that unnamed arguments are to be added up to the limit on the number of arguments. These can be accessed using the ARG() function and the SET.ARG statement. See the PUBLIC statement for more details of this feature.
It is valid for a class module to contain combinations of a PUBLIC variable, PUBLIC SUBROUTINE and PUBLIC FUNCTION with the same name. If there is a public subroutine of the same name as a public variable, the subroutine will be executed when a program using the object attempts to set the value of the public item. If there is a public function of the same name as a public variable, the function will be executed when a program using the object attempts to retrieve the value of the public item. If both are present, the public property variable will never be directly visible to programs using the object.
Sometimes an application developer may wish a public variable to be visible to users of the class for reading but not for update. Although this could be achieved by use of a dummy PUBLIC SUBROUTINE that ignores updates or reports an error, public variables may be defined as read-only by including the READONLY keyword after the variable declaration: PUBLIC A READONLY or PUBLIC B(5) READONLY
Referencing an Object
References to an object require two components, the object variable and the name of a property or method within that object. The syntax for such a reference is OBJ->PROPERTY or, if arguments are required, OBJ->PROPERTY(ARG1, ARG2, ...)
When used in a QMBasic expression, for example, ITEMS += OBJ->LISTCOUNT the object reference returns the value of the named item, in this case LISTCOUNT. This may be a public variable or the value of a public function. If the same name is defined as both, the public function is executed.
When used on the left of an assignment, for example, OBJ->WIDTH = 70 the object reference sets the value of the named item, in this case WIDTH. This may be a public variable or the value of a public subroutine that takes the value to be assigned as an argument. If the same name is defined as both, the public subroutine is executed.
This dual role of public variables and functions or subroutines makes it very easy to write a class module in which, for example, a property value may be retrieved without execution of any program statements inside the object but setting the value executes a subroutine to validate the new value.
All object, property and public routine names are case insensitive.
Using Dimensions and Arguments
Public variables may be dimensioned arrays. Subscripts for index values are handled in the usual way: OBJ->MODE(3) = 7 where MODE has been defined as a single dimensional array. If MODE has an associated public subroutine, the indices are passed via the arguments and the new value as the final argument. Thus, if MODE was defined as PUBLIC SUBROUTINE MODE(A,B) the above statement would pass in A as 3 and B as 7.
Execution of Object Methods
Other object oriented languages usually provide methods, subroutines that can be executed from calling programs to do some task. QMBasic class modules do this by using public subroutines. The calling program uses a statement of the form: OBJ->RESET where RESET is the name of the public subroutine representing the method. Again, arguments are allowed: OBJ->RESET(5)
This leads to an apparent syntactic ambiguity between assigning values to public properties and execution of methods. Actually, there is no ambiguity but the following two statements are semantically identical: OBJ->X(2,3) OBJ->X(2) = 3
Expressions as Property Names
All of the above examples have used literal (constant) property names. QMBasic allows expressions as property names in all contexts using a syntax OBJ->(expr) where expr is an expression that evaluates to the property name.
Object References in Subroutine Calls
Any reference to an object element in a subroutine call, for example CALL SUBNAME(OBJ->VAR) is considered to be read access. If the subroutine updates the argument, this will not update the object property value.
The ME Token
Sometimes an object needs to reference itself. The reserved data name ME can be used for this purpose: ME->RESET
The CREATE.OBJECT Subroutine
When an object is instantiated using the OBJECT() function, part of this process checks whether there is a public subroutine named CREATE.OBJECT and, if so, executes it. This can be used, for example, to preset default values in public and private variables. Up to 32 arguments may be passed into this subroutine by extending the OBJECT() call to include these after the catalogue name of the class module.
The DESTROY.OBJECT Subroutine
An object remains in existence until the last object variable referencing it is discarded or overwritten. At this point, the system checks for a public subroutine named DESTROY.OBJECT and, if it exists, it is executed.
The UNDEFINED Name Handler
The optional UNDEFINED public subroutine and/or public function can be used to trap references to the object that use property names that are not defined. This handler is executed if a program using the object references a name that is not defined as a public item. The first argument will be the undefined name. Any arguments supplied by the calling program will follow this. The ARG.COUNT() and ARG() functions can be used to help extract this data in a meaningful way.
If there is no UNDEFINED subroutine/function, object references with undefined names cause a run time error.
Inheritance
Sometimes it is useful for one class module to incorporate the properties and methods of another. This is termed inheritance.
Use of the INHERITS clause of the CLASS statement effectively inserts declaration of a private variable of the same name as the inherited class (removing any global catalogue prefix character) and adds name = OBJECT(inherited.class) INHERIT name to the CREATE.OBJECT subroutine.
Alternatively, inheritance can be performed during execution of the object by direct use of the INHERIT statement.
The name search process that occurs when an object is referenced scans the name table of the original object reference first. If the name is not found, it then goes on to scan the name tables of each inherited object in the order in which they were inherited. Where an inherited object has itself inherited further objects, the lower levels of inheritance are treated as part of the object into which they were inherited. If the name is not found, the same search process is used to look for the undefined name handler.
An inherited object can subsequently be disinherited using DISINHERIT.
Syntax Summary
CLASS name {INHERITS class1, class2...} PUBLIC A {READONLY}, B(3), C PRIVATE X, Y, Z
PUBLIC SUBROUTINE SUB1(ARG1, ARG2) {VAR.ARGS} ...processing... END
PUBLIC FUNCTION FUNC1(ARG1, ARG2) {VAR.ARGS} ...processing... RETURN RESULT END
...Other QMBasic subroutines... END
See also: |