Difference between revisions of "Object-Oriented Programming in FreeM"

From FreeM Wiki
Jump to navigation Jump to search
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
 +
''Please note that object-oriented features exist only in the development branch of FreeM, and are neither complete nor available in the current official releases. Also, full documentation will be added to the official FreeM Manual when OO features are officially released. - Serena''
 +
 
= Classes =
 
= Classes =
  
 
In FreeM, a class is defined by a routine. For instance, the <code>STRING</code> class (built into FreeM) is contained in the <code>%STRING</code> routine.
 
In FreeM, a class is defined by a routine. For instance, the <code>STRING</code> class (built into FreeM) is contained in the <code>%STRING</code> routine.
  
== Example ==
+
 
 +
== Constructors ==
 +
A constructor must be the first entry point in a class definition, and the label name must match the routine name, and it must take two arguments, <code>THIS</code> and <code>INIT</code>. <code>THIS</code> represents the instance of the object being accessed, and <code>INIT</code> represents an initializer that can be used to assign an initial value to the object when instantiating the class.
 +
 
 +
A constructor looks like this:
  
 
<pre>
 
<pre>
Line 10: Line 16:
 
   S THIS("DENOMINATOR"):PRIVATE=$P(INIT,"/",2)
 
   S THIS("DENOMINATOR"):PRIVATE=$P(INIT,"/",2)
 
   Q
 
   Q
GCD(THIS) Q $$GCDI^%FRACTION(THIS("NUMERATOR"),THIS("DENOMINATOR"))
+
</pre>
 +
 
 +
The general syntax of a constructor is <code>&lt;routine-name&gt;(THIS,INIT)[:&lt;superclass&gt;]</code>, where <code>superclass</code> represents the name of a class from which this class should inherit. In the above example, the <code>FRACTION</code> class inherits from the <code>OBJECT</code> class. Note that this is not strictly necessary in this case, as all classes automatically inherit from <code>OBJECT</code>.
 +
 
 +
== Destructors ==
 +
A destructor is called when you <code>KILL</code> an instance variable. It must be named <code>DESTROY</code>, and must take one argument (<code>THIS</code>). The destructor should be used to clean up any resources used by class methods.
 +
 
 +
A destructor looks like this:
 +
 
 +
<pre>
 +
DESTROY(THIS) ;
 +
  ; free any resources that should be freed at the end of the object's lifetime
 +
  Q
 +
</pre>
 +
 
 +
== Inheritance Rules ==
 +
Every class you create will automatically inherit the methods and functionality of the <code>OBJECT</code> class, supplied with FreeM.
 +
 
 +
When attempting to call a method, FreeM will first search the class routine for a matching entry point, and then follow the inheritance chain upwards until a matching entry point is found. If the final class in the chain does not have a matching entry point, FreeM will try to find a matching entry point in the <code>OBJECT</code> class.
 +
 
 +
== Instantiating Classes ==
 +
To instantiate a class (i.e., create an object from a certain class), you will use the <code>NEW</code> command as follows:
 +
 
 +
<pre>
 +
NEW MYSTR=$#^%STRING("myString")
 +
</pre>
 +
 
 +
This will create a local variable called <code>MYSTR</code> of type <code>STRING</code>, and initialize it with the value <code>myString</code>.
 +
 
 +
== Defining Class Methods ==
 +
 
 +
Class methods are defined as labels with formallists in a class routine, and per the typical FreeM object pattern, must take at least one argument, being <code>THIS</code> (representing a reference to the object instance being accessed).
 +
 
 +
The following class (<code>MYCLASS</code>) has a constructor, a destructor, and a method called <code>$$MYMETHOD</code>:
 +
 
 +
<pre>
 +
%MYCLASS(THIS,INIT) ;
 +
  Q THIS
 
DESTROY(THIS) ;
 
DESTROY(THIS) ;
Q
+
  Q
REDUCE(THIS) ;
+
MYMETHOD(THIS) ;
N G,NUMR,DENR
+
  Q "VALUE"
S G=$$GCDI^%FRACTION(THIS("NUMERATOR"),THIS("DENOMINATOR"))
 
S NUMR=THIS("NUMERATOR")/G
 
S DENR=THIS("DENOMINATOR")/G
 
Q NUMR_"/"_DENR
 
;
 
GCDI(A,B) Q:B=0 A S R=$$GCDI^%FRACTION(B,A#B) Q R
 
 
</pre>
 
</pre>
  
 +
== Runtime Polymorphism with Method Overriding ==
 +
You can achieve runtime polymorphism by subclassing, and defining methods in the subclass that match the names of existing methods in the superclass. Following FreeM inheritance rules, the overridden method in the subclass will be called, and the method in the superclass will not.
  
== Constructors ==
+
Note that the overridden method in the subclass can take a different set or number of arguments than the formallist of the superclass would specify.
A constructor must be the first entry point in a class definition, and the label name must match the routine name, and it must take two arguments, <code>THIS</code> and <code>INIT</code>. <code>THIS</code> represents the instance of the object being accessed, and <code>INIT</code> represents an initializer that can be used to assign an initial value to the object when instantiating the class.
+
 
 +
== Calling Class Methods ==
 +
The dot operator is used to invoke class methods:
 +
 
 +
<pre>
 +
USER> N MYOBJ=$#^%MYCLASS("")
 +
USER> W MYOBJ.MYMETHOD()
 +
VALUE
 +
</pre>
 +
 
 +
== Determining an Object's Class at Run-Time ==
 +
 
 +
To determine the class of any FreeM local variable, you will use the <code>$$TYPE()</code> method:
 +
 
 +
<pre>
 +
USER> W MYSTR.$$TYPE()
 +
^%STRING
 +
</pre>
 +
 
 +
The <code>$$TYPE()</code> method is a member of the <code>OBJECT</code> class.
 +
 
 +
== Default Class ==
 +
 
 +
M local variables not explicitly instantiated as objects will always appear to be objects of the <code>^%STRING</code> class, which is supplied with the FreeM distribution.
 +
 
 +
== Retrieving an Object's Value ==
 +
 
 +
To retrieve the value of a FreeM object, either dereference the object via the usual M mechanisms, or use the <code>$$VALUE()</code> method:
 +
 
 +
<pre>
 +
USER> W MYSTR
 +
myString
 +
USER> W MYSTR.$$VALUE()
 +
myString
 +
USER>
 +
</pre>
 +
 
 +
For simple classes, the value of an object retrieved with either method should be the same. However, for more complex classes that override the <code>OBJECT</code> class's <code>$$VALUE()</code> method, they may be different.
 +
 
 +
== Retrieve Numeric Representation of Object ==
 +
To retrieve a numeric representation of a FreeM object, use the <code>$$TONUMBER()</code> method. Note that this may not retrieve anything if the object has no meaningful numeric representation.
 +
 
 +
== Private Class Fields ==
 +
FreeM supports private fields with the <code>:PRIVATE</code> specifier in the <code>SET</code> command, enforcing classical object-oriented data encapsulation.
 +
 
 +
The below constructor for a <code>FRACTION</code> class defines two private fields:
 +
 
 +
<pre>
 +
%FRACTION(THIS,INIT):OBJECT ;
 +
  S THIS("NUMERATOR"):PRIVATE=$P(INIT,"/",1)
 +
  S THIS("DENOMINATOR"):PRIVATE=$P(INIT,"/",2)
 +
  Q
 +
</pre>
  
On the first line of the above example, <code>:OBJECT</code> indicates that the <code>FRACTION</code> class inherits from <code>OBJECT</code>
+
Attempting to access private fields from outside of the class will raise error condition <code>ZOBJFLDACCV</code>.

Latest revision as of 20:57, 29 November 2024

Please note that object-oriented features exist only in the development branch of FreeM, and are neither complete nor available in the current official releases. Also, full documentation will be added to the official FreeM Manual when OO features are officially released. - Serena

Classes

In FreeM, a class is defined by a routine. For instance, the STRING class (built into FreeM) is contained in the %STRING routine.


Constructors

A constructor must be the first entry point in a class definition, and the label name must match the routine name, and it must take two arguments, THIS and INIT. THIS represents the instance of the object being accessed, and INIT represents an initializer that can be used to assign an initial value to the object when instantiating the class.

A constructor looks like this:

%FRACTION(THIS,INIT):OBJECT ;
   S THIS("NUMERATOR"):PRIVATE=$P(INIT,"/",1)
   S THIS("DENOMINATOR"):PRIVATE=$P(INIT,"/",2)
   Q

The general syntax of a constructor is <routine-name>(THIS,INIT)[:<superclass>], where superclass represents the name of a class from which this class should inherit. In the above example, the FRACTION class inherits from the OBJECT class. Note that this is not strictly necessary in this case, as all classes automatically inherit from OBJECT.

Destructors

A destructor is called when you KILL an instance variable. It must be named DESTROY, and must take one argument (THIS). The destructor should be used to clean up any resources used by class methods.

A destructor looks like this:

DESTROY(THIS) ;
  ; free any resources that should be freed at the end of the object's lifetime
  Q

Inheritance Rules

Every class you create will automatically inherit the methods and functionality of the OBJECT class, supplied with FreeM.

When attempting to call a method, FreeM will first search the class routine for a matching entry point, and then follow the inheritance chain upwards until a matching entry point is found. If the final class in the chain does not have a matching entry point, FreeM will try to find a matching entry point in the OBJECT class.

Instantiating Classes

To instantiate a class (i.e., create an object from a certain class), you will use the NEW command as follows:

NEW MYSTR=$#^%STRING("myString")

This will create a local variable called MYSTR of type STRING, and initialize it with the value myString.

Defining Class Methods

Class methods are defined as labels with formallists in a class routine, and per the typical FreeM object pattern, must take at least one argument, being THIS (representing a reference to the object instance being accessed).

The following class (MYCLASS) has a constructor, a destructor, and a method called $$MYMETHOD:

%MYCLASS(THIS,INIT) ;
  Q THIS
DESTROY(THIS) ;
  Q
MYMETHOD(THIS) ;
  Q "VALUE"

Runtime Polymorphism with Method Overriding

You can achieve runtime polymorphism by subclassing, and defining methods in the subclass that match the names of existing methods in the superclass. Following FreeM inheritance rules, the overridden method in the subclass will be called, and the method in the superclass will not.

Note that the overridden method in the subclass can take a different set or number of arguments than the formallist of the superclass would specify.

Calling Class Methods

The dot operator is used to invoke class methods:

USER> N MYOBJ=$#^%MYCLASS("")
USER> W MYOBJ.MYMETHOD()
VALUE

Determining an Object's Class at Run-Time

To determine the class of any FreeM local variable, you will use the $$TYPE() method:

USER> W MYSTR.$$TYPE()
^%STRING

The $$TYPE() method is a member of the OBJECT class.

Default Class

M local variables not explicitly instantiated as objects will always appear to be objects of the ^%STRING class, which is supplied with the FreeM distribution.

Retrieving an Object's Value

To retrieve the value of a FreeM object, either dereference the object via the usual M mechanisms, or use the $$VALUE() method:

USER> W MYSTR
myString
USER> W MYSTR.$$VALUE()
myString
USER>

For simple classes, the value of an object retrieved with either method should be the same. However, for more complex classes that override the OBJECT class's $$VALUE() method, they may be different.

Retrieve Numeric Representation of Object

To retrieve a numeric representation of a FreeM object, use the $$TONUMBER() method. Note that this may not retrieve anything if the object has no meaningful numeric representation.

Private Class Fields

FreeM supports private fields with the :PRIVATE specifier in the SET command, enforcing classical object-oriented data encapsulation.

The below constructor for a FRACTION class defines two private fields:

%FRACTION(THIS,INIT):OBJECT ;
   S THIS("NUMERATOR"):PRIVATE=$P(INIT,"/",1)
   S THIS("DENOMINATOR"):PRIVATE=$P(INIT,"/",2)
   Q

Attempting to access private fields from outside of the class will raise error condition ZOBJFLDACCV.