8.1.2 Com_Owner, Com_Ancestor and Com_Self - Generic References to Components
Com_Owner
Com_Owner is commonly used throughout RDMLX and is a variable that always refers to the object in which the command is specified.
So, when adding controls to a component using the designer, or implementing events and method routines, any source code generated by the editor will refer to the component currently being edited using the generic name #COM_OWNER.
Define_Com Class(#PRIM_LABL) Name(#Label) Parent(#COM_OWNER)
OR
Evtroutine Handling(#Com_Owner.Initialize)
#Com_owner.Prepare
Endroutine
As a large percentage of code typically needs to refer to the current location, the use of the generic Com_owner to mean 'here' greatly simplifies code understanding. It also means that copied code can be pasted directly in to a target without the need to update component references with specific class names.
For relatively simple applications that don't use inheritance beyond a single level, there is no reason to move away from the use of Com_Owner. However, where multiple layers of inheritance are being used, Com_Ancestor and Com_Self can be used to execute code defined in the different layers of the inheritance hierarchy.
Com_Ancestor
Com_Ancestor refers directly to the class specified as the ancestor of the current component.
Thus, when in an application that uses inheritance, we might define basic behavior in a base ancestor class but override that behavior by redefining the method in the inheriting class. At execution time the processing in the ancestor class will be ignored and the redefined method will be executed.
Mthroutine Name(Prepare) Options(*Redefine)
* Class specific processing
Endroutine
Whilst this typical use of a redefined method is often desirable, there may also be circumstances where rather than wanting to completely change the processing, we simply need to augment it. To enable this we might write the following
Mthroutine Name(Prepare) Options(*Redefine)
#Com_Ancestor.Prepare
* Class specific processing
Endroutine
Here, the method is redefined but the very first line of the new method causes the ancestor version of the method to be executed. This technique is typically used when a change to the ancestor processing is the exception rather than the rule.
Com_Self
Com_Self and Com_owner are very similar in use apart from one very distinct difference. While Com_owner always refers to the current component, Com_Self refers to the current component taking into consideration any redefined methods further up the inheritance chain.
In the previous Com_ancestor example, redefining the method represented the exceptional case, but in a scenario where it is commonplace, the need to remember to execute ancestor code can cause complications. In this situation it is better to drive the processing from the ancestor class.
Thus in the Ancestor we might have something similar to the following.
Mthroutine Name(Prepare)
* Run base code then class specific code
#Com_owner.PrepareBase
#Com_Self.PrepareSelf
Endroutine
Mthroutine Name(PrepareBase) Options(*Final)
* Base class processing
* Final – This method cannot be redefined
Endroutine
Mthroutine Name(PrepareSelf)
* Redefine in inheriting classes
Endroutine
In the inheriting class we would then see:
Mthroutine Name(PrepareSelf) Options(*Redefine)
* Class specific processing here
Endroutine
At runtime, invoking the Prepare method would result in the PrepareBase method being executed. However, because the invocation of PrepareSelf uses Com_Self, the runtime looks further up the inheritance chain for a redefined version and executes that rather than the version of the method defined in the ancestor.
Unfortunately, it is all too easy to accidentally use Com_owner rather than Com_Self and a common issue found during development is that redefined code is never actually executed because Com_owner has been used instead of Com_Self.