Object Oriented Programming

Object Oriented Programming

Top  Previous  Next

 

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. A clone of an existing object can be created by use of the OBJECT() function in which the argument is a reference to the object that is to be cloned.

 

 
Class Module Variables

 

There are five styles of data storage available in a class module.

 

Public variables are declared using the PUBLIC statement, for example,

PUBLIC A, B(5)

at the start of the class module before any executable program statements. Each instance of a class has its own separate public variables but the values persist between entries to that object instance. Public variables are initially unassigned and may be accessed from within the class module or, subject to rules set out below, from outside.

 

Private variables are declared using the PRIVATE statement, for example,

PRIVATE C, D, E

at the start of the class module before any executable program statements. Each instance of a class has its own separate private variables but the values persist between entries to the object instance. Private variables are initially unassigned and may be accessed only from within the class module.

 

Shared variables are declared using the SHARED statement, for example,

SHARED PUBLIC F, G(10), H

at the start of the class module before any executable program statements. Each instance of the same class shares the same variables. Values stored in shared variables persist until the last reference to the class is discarded. Shared variables are initially unassigned and may be defined as either private or public.

 

Common variables are declared using the COMMON statement, for example,

COMMON I, J(8)

or

COMMON /NAME/ K, L

at the start of the class module before any executable program statements. As with use of common variables in other program module types, common variables are accessible to all programs, subroutines, functions and objects within the QM process. Common variables are initially set to zero unless the UNASSIGNED.COMMON setting of the $MODE compiler directive is enabled. Variables in an unnamed common are discarded on return to the command processor, those in named common persist until the QM session terminates or the common is explicitly deleted using the DELETE.COMMON command.

 

Local variables are either not declared at all or are declared using a DIMENSION (DIM) statement. As in other program types, they exist only while the object instance in which they appear is active and are discarded on leaving the program. Local variables are accessible only from within the object instance and are initially unassigned.

 

Private and public data items are frequently used to store what other object oriented programming environments would term property values. These data items are initially unassigned.

 

 

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 subroutine name and A, B and C are the arguments (optional). A whole matrix can be referenced as an argument by following it with the dimension values. For example,

PUBLIC SUBROUTINE CALC(CLIENT, CLI.REC(1), TOTVAL)

In this example, the dimension value has been shown as 1 to emphasise that the actual value is irrelevant. The compiler uses this purely to determine that CLI.REC is a single dimensional matrix, possibly representing a database record read using MATREAD. The alternative syntax used with SUBROUTINE statements by prefixing the matrix name with MAT and using a DIMENSION statement to set dimensionality is not available for public subroutines and functions.

 

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 scalar 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, ...)

 

Any argument may reference a whole matrix by prefixing the matrix name with the keyword MAT, for example

OBJ->CALC(CLIENT, MAT CLI.REC, TOTVAL)

 

 

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 and is passed by value. 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

Note that setting a private or public variable inside the object to the ME reference, for example

PUBLIC MYSELF

MYSELF = ME

would create a situation where the object can never be discarded as there is always a reference to it.

 

 

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. This subroutine is guaranteed to be executed, even if the object variable is discarded as part of a program failure that causes an abort. The only situation where an object can cease to exist without this subroutine running to completion is if the DESTROY.OBJECT subroutine itself aborts.

 

 

The MAIN Subroutine

 

If a class module includes a public subroutine named MAIN, an object instantiated from that class can be executed as a main program from the command processor using RUN or a catalogue reference in exactly the same way as a PROGRAM module but cannot be executed using CALL. The CREATE.OBJECT subroutine, if present, will be executed followed by the MAIN subroutine.

 

 

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.

 

 

Debugging an Object Instance

 

The names of arguments to a public subroutine are not known to the debugger. Instead, a special positional reference of the form "*Arg1" is needed when displaying argument variables. The *Arg prefix is case insensitive and the argument number may include leading zeroes. For example, display of the second argument could use a debugger command such as

/*arg2

All of the qualifiers to variable references such as array indices and dynamic array element positions may be used.

 

 

Syntax Summary

 

CLASS name {INHERITS class1, class2...}

  PUBLIC A {READONLY}, B(3), C

  PRIVATE P, Q, R
  SHARED 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

 

 

There is a sample QMBasic class module named INDEX.CLS in the BP file of the QMSYS account, catalogued as !INDEX.CLS. This class allows an application to scan an index one record id at a time instead of one indexed value at a time. Full details of its use and internal operation can be found in the source code.

 

 

See also:

CLASS, DISINHERIT, INHERIT, OBJECT(), PRIVATE, PUBLIC.