INHERITANCE ISSUES:
-------------------
In oObject the ::super:<method> type of inheritance is not really used.
Instead, oObject uses a technique called "method aliasing". This technique
requires that every new class has its own "alias", a pseudonym that oObject
uses to internally rename parent methods and transfer them in the child
classes e.g.
(1) CLASS Parent Alias oP
...
METHOD Display()
...
ENDCLASS
(2) CLASS Child ALIAS oC
...
METHOD Display()
...
ENDCLASS
In this example, class "child" has 2 ::display() methods, its own
::display() and its parent's ::oPDisplay() method. Parent methods that
don't share the same name as child classes are transfered unaliased.
This technique ensures that each new class is self contained and does not
really reference its parent except at creation time. Other OOP libraries
keep static arrays that are scanned to find the correct parent:method. This
is not necessary in oObject since each class contains all methods it will
ever need - except parent internal and local METHODS/IVARS.
This however raises another issue. Classes that carry a huge number of
messages. oObject supports up to 250 Instance Variables & INLINE-BLOCK
statements and unlimited PUBLIC METHODS (STATIC Methods are implemented
using INLINE statements) but its up to YOU to keep your classes small and
fast. Use object subclassing (an object's IVar is a reference to another
object) to avoid unnecessary inheritance.
For compatibility, each class carries a reference to its parent in the
::DICT:Super IVar and in the ::Super IVar. Accessing parent classes
using ::Super:<Message> is slower than using directly the ALIASed
parent Method.
Also, to access ::super using ::Super:<Message> it is necessary to first
initialize all the IVars that ::Super:<Message> is likely to need, namely
its formal parameters, since the parent class has not been properly
initialized in the child class. ( see "TestSta*.prg" in the EXAMPLES
directory )
Starting with version 2.Ab oObject also suports true inheritance of
Clipper's native classes (not subclassing). Using this feature will also
result in classes with too many messages.
Note: the implementation of this technique in version 2.B ensures that
native classes Methods are inherited as true PUBLIC Methods and IVars as
IVars (see ooBject.prg - METHOD __SuperMethod() ).
VARIABLE ASSIGNMENT:
--------------------
You may store default values to instance variables when initializing the
new class; that means between the CLASS <cClass>... and ENDCLASS
statements when declaring new variables e.g.
VAR test1 AS INT // test1 is 0
VAR test2 AS FLOAT // test2 is .F.
VAR test3 AS DATE // test3 is CTOD(" / / ")
VAR test4 AS TBrowseDB() // test4 is TBrowse object
...etc...
ENDCLASS
For some types of variables, this is very important as you may (a) save
several lines of initialization code from your CONSTRUCTOR method and (b)
READONLY variables that must be inherited by other classes MUST have a
value upon class initialization.
Note however that at some cases this feature is not only unwanted but
can also produce some erroneous results, e.g. a counter that is
incremented in the first instance of the class and keeps its value to
the next instance, resembling the behaviour of a STATIC variable.
That is of course, if your INIT/Constructor function does not assign values
to class variables.
It's up to you to decide which IVars to initialise in the constructor
function and which not...
Especially for READONLY variables, they can be assigned new values only
from within their class and only in two ways:
a) Assign a value at class declaration, between the CREATE CLASS and
ENDCLASS commands
b) Assign a value using the @: operator, to directly modify the self array
element that points to the variable (see "Direct modification of
instance variables - early binding" bellow).
USER DEFINED GET/SET IVARS
~~~~~~~~~~~~~~~~~~~~~~~~~~
Every Instance variable is infact assigned and retrieved using a GET and a
SET function or a combined GET/SET Function.
To use this feature you must directly reference the Self ELEMENT you want
to work with from within your GET/SET function. This means you can not use
the send operator (::) to assign or retrieve the value of the IVar at hand.
This would result in a "processor stack" recursion error.
You may of course reference other class IVars using the send operator, for example.
#translate GETSET( <xVal>, <xParm> ) => ;
<xVal> := IF( xVal==NIL, <xVal>, <xVal> := <xParm>)
CLASS Test
DATA Mydata1 GETSET My1SpecialFunc // No 1 self element
DATA Mydata2 GETSET My2SpecialFunc // No 2 self element
...
ENDCLASS
FUNCTION My1SpecialFunc( xVal )
IF xVal == NIL
RETURN QSelf[1]
ELSE
QSelf()[1] := SomeFunc( xVal )
ENDIF
RETURN QSelf()[1]
FUNCTION My2SpecialFunc( xVal )
RETURN GETSET( QSelf()[2], xVal )