Enhancement methods: Difference between revisions

From m204wiki
Jump to navigation Jump to search
 
(18 intermediate revisions by 5 users not shown)
Line 6: Line 6:
[[Classes and Objects#Declaration blocks|public and private]]
[[Classes and Objects#Declaration blocks|public and private]]
variables in the class.
variables in the class.
For system classes, this data is hidden from <var class="product">User Language</var> programs, but it is
For system classes, this data is hidden from <var class="product">SOUL</var> programs, but it is
stored more or less identically to <var class="product">User Language</var> variables.
stored more or less identically to <var class="product">SOUL</var> variables.
<li>The operations that can be performed on objects of that class.
<li>The operations that can be performed on objects of that class.
These operations are the methods
These operations are the methods
Line 18: Line 18:
on objects of the class that are ''not'' one of the standard methods
on objects of the class that are ''not'' one of the standard methods
provided by the class.
provided by the class.
Before <var class="product">Sirius Mods</var> version 7.2, there were three ways of addressing this:
<!-- Before <var class="product">Sirius Mods</var> version 7.2 -->
Before the introduction of enhancement methods, there were three ways of addressing this:
<ol>
<ol>
<li>Add a new method to the class.
<li>Add a new method to the class.
Line 29: Line 30:
<ul>
<ul>
<li>If the class is a system class, new methods cannot be added
<li>If the class is a system class, new methods cannot be added
using <var class="product">User Language</var>.
using <var class="product">SOUL</var>.
 
<li>If the class belongs to a different group in the organization,
<li>If the class belongs to a different group in the organization,
it can be problematic getting a new method added.
it can be problematic getting a new method added.
<li>The method being added might need access to private variables
<li>The method being added might need access to private variables
in another class, so the method must be added to the other class, not
in another class, so the method must be added to the other class, not
Line 42: Line 45:
[[Intrinsic classes|intrinsic classes]],
[[Intrinsic classes|intrinsic classes]],
and XmlNodes are not allowed to be extended.
and XmlNodes are not allowed to be extended.
<li>It requires changing variable declarations to use the new class.
<li>It requires changing variable declarations to use the new class.
If the variables, themselves, are parameters, callers might have to be
If the variables, themselves, are parameters, callers might have to be
Line 47: Line 51:
This can produce a cascade effect where large chunks of code have to
This can produce a cascade effect where large chunks of code have to
be changed to add an extra method to a class.
be changed to add an extra method to a class.
<li>While inheritance is implemented quite efficiently, there '''is'''
<li>While inheritance is implemented quite efficiently, there '''is'''
a cost in code complexity to using inheritance, and using inheritance
a cost in code complexity to using inheritance, and using inheritance
Line 96: Line 101:
</p>
</p>
   
   
<var class="product">Sirius Mods</var> version 7.2 introduced a new type of shared method called an
<!-- <var class="product">Sirius Mods</var> version 7.2 introduced -->
'''enhancement method''' to provide a better way of invoking a
A new type of shared method called an
'''enhancement method''' has provided a better way of invoking a
method on an object of another class.
method on an object of another class.
Because enhancement methods do not operate on objects of the containing
Because enhancement methods do not operate on objects of the containing
Line 110: Line 116:
<table class="syntaxTable">
<table class="syntaxTable">
<tr><th>method
<tr><th>method
</th><td>The method type &mdash; <var>Subroutine</var>, <var>Function</var>, or <var>Property</var>. An enhancement method cannot be a <var>[[Janus SOAP User Language Interface#constructor|Constructor]]</var>.
</th><td>The method type &mdash; <var>Subroutine</var>, <var>Function</var>, or <var>Property</var>. An enhancement method cannot be a <var>[[Object oriented programming in SOUL#constructor|Constructor]]</var>.
</td></tr>
</td></tr>
<tr><th>class
<tr><th>class
Line 131: Line 137:
</th><td>An object variable of the class against which the enhancement method operates.
</th><td>An object variable of the class against which the enhancement method operates.
</td></tr>
</td></tr>
<tr><th>+containerClass
<tr><th>+containerClass
</th><td>The name of the class that contains the enhancement method definition. Note that the class name must be preceded by a plus sign (<tt>+</tt>) to indicate an enhancement method invocation. The plus sign distinguishes an enhancement method container class name from other uses of class names inside parentheses that might appear in the same context.
</th><td>The name of the class that contains the enhancement method definition. Note that the class name must be preceded by a plus sign (<tt>+</tt>) to indicate an enhancement method invocation. The plus sign distinguishes an enhancement method container class name from other uses of class names inside parentheses that might appear in the same context.
</td></tr>
</td></tr>
<tr><th>name
<tr><th>name
</th><td>The name of the enhancement method.
</th><td>The name of the enhancement method.
</td></tr>
</td></tr>
<tr><th>parameters
 
<tr><th>arguments
</th><td>Any arguments the enhancement method might take as input. Optional, default, and named parameters work exactly the same way with enhancement methods as with any other methods.
</th><td>Any arguments the enhancement method might take as input. Optional, default, and named parameters work exactly the same way with enhancement methods as with any other methods.
</td></tr></table>
</td></tr></table>
   
   
For example, to declare and define an enhancement method version of
For example, to declare and define an enhancement method version of
the </code>EveryOther<code> method described earlier, one would do something like
the <code>EveryOther</code> method described earlier, one would do something like
the following:
the following:
<p class="code">class utility
<p class="code">class utility
Line 164: Line 173:
As can be seen in this example, an enhancement method has an
As can be seen in this example, an enhancement method has an
implicitly declared object variable called <code>%this</code>.
implicitly declared object variable called <code>%this</code>.
As with unshared methods, the </code>%this<code> variable is a reference
As with unshared methods, the <code>%this</code> variable is a reference
to the method object.
to the method object.
Unlike unshared methods, the </code>%this<code> variable is not an object of
Unlike unshared methods, the <code>%this</code> variable is not an object of
the containing class, but is, instead, an instance of the class
the containing class, but is, instead, an instance of the class
to which the enhancement method applies.
to which the enhancement method applies.
Line 185: Line 194:
This chain of methods can be read from left to right
This chain of methods can be read from left to right
rather than from the inside out.
rather than from the inside out.
===Invoking a Local Alias of an enhancement method===
You can also define a <var>[[Local and Common entities#Local aliases|Local Alias]]</var> of an enhancement method, and invoke the enhancement method using the alias:
<p class="code">local alias (stringlist):everyOther for (+utility)everyOther
...
%mylist:everyOther:print
</p>
This results in a much more natural calling syntax, as though the user-written enhancement method were an integral component of the enhanced class.
==Enhancement methods and inheritance==
==Enhancement methods and inheritance==
Enhancement methods are never automatically inherited by extension
Enhancement methods are never automatically inherited by extension
Line 190: Line 210:
the <var>Inherit</var> keyword is specified on the class declaration for the
the <var>Inherit</var> keyword is specified on the class declaration for the
extension class.
extension class.
For example, the </code>EveryOther<code> method
For example, the <code>EveryOther</code> method
in the preceding section cannot be directly applied
in the preceding section cannot be directly applied
to </code>%mylist<code> if </code>%mylist<code> is actually an object of class </code>Funnylist<code>,
to <code>%mylist</code> if <code>%mylist</code> is actually an object of class <code>Funnylist</code>,
where </code>FunnyList<code> is an extension of class <var>Stringlist</var>.
where <code>FunnyList</code> is an extension of class <var>Stringlist</var>.
However, the method can be applied by explicitly specifying
However, the method can be applied by explicitly specifying
the name of the class to which the method applies:
the name of the class to which the method applies:
Line 212: Line 232:
</p>
</p>
   
   
If this were specified, the </code>EveryOther<code> enhancement method could
If this were specified, the <code>EveryOther</code> enhancement method could
be applied to objects of class </code>Funnylist<code> without any extra
be applied to objects of class <code>Funnylist</code> without any extra
qualification.
qualification.
<br>'''Note:'''
<blockquote class="note">'''Note:'''
Because an enhancement method is not defined
Because an enhancement method is not defined
in the class to which the method applies, it cannot reference private
in the class to which the method applies, it cannot reference private
variables in that class.
variables in that class.
However, it '''can''' reference private members of instances
However, it '''can''' reference private members of instances
of objects of the containing class.
of objects of the containing class.  
<p>
In fact, one typical application for enhancement methods is to
In fact, one typical application for enhancement methods is to
provide a method for creating a new instance of the containing
provide a method for creating a new instance of the containing
Line 228: Line 248:
Such a method might not need to access private members of the
Such a method might not need to access private members of the
method object class, but it might need to access private members of
method object class, but it might need to access private members of
the containing class, so an enhancement factory method is
the containing class, so an enhancement factory method is perfectly suitable. </p>
perfectly suitable.
</blockquote>
==Intrinsic Enhancement methods==
 
In addition to Enhancement methods, <var class="product">Sirius Mods</var> 7.2 introduced
==Intrinsic enhancement methods==
<var class="syntax">[[Intrinsic classes|Intrinsic]]</var> methods and classes.
Just as you can create enhancement methods for other system
And, just as you can create enhancement methods for other system
classes, you can create enhancement methods for the [[Intrinsic classes|intrinsic]] classes,
classes, you can create enhancement methods for intrinsic classes,
specifically for the <var>Float</var> and <var>String</var> classes.
specifically for the <var>Float</var> and <var>String</var> classes.
   
   
Line 260: Line 279:
The preceding statement sets <code>%hyp</code> to 5.
The preceding statement sets <code>%hyp</code> to 5.
As can be seen in this example, for a <var>Float</var> enhancement method, the
As can be seen in this example, for a <var>Float</var> enhancement method, the
implicitly defined </code>%this<code> variable has a <var>Float</var> datatype.
implicitly defined <code>%this</code> variable has a <var>Float</var> datatype.
   
   
The following is an example of a <var>String</var> enhancement method that returns
The following is an example of a <var>String</var> enhancement method that returns
Line 295: Line 314:
a need to create an enhancement method on a collection of that class.
a need to create an enhancement method on a collection of that class.
   
   
For example, you might have an </code>Order<code> class that describes an order
For example, you might have an <code>Order</code> class that describes an order
for widgets.
for widgets.
You might want to provide an enhancement method on </code>Arraylist of
You might want to provide an enhancement method on <code>Arraylist of
Order<code> objects that creates a new <var>Arraylist</var> of objects that contains
Order</code> objects that creates a new <var>Arraylist</var> of objects that contains
items that need to be back-ordered (there are insufficient widgets
items that need to be back-ordered (there are insufficient widgets
in stock to satisfy the order).
in stock to satisfy the order).
Line 352: Line 371:
==See also==
==See also==
<ul>
<ul>
<li>[[Methods]]
<li>[[Inheritance and polymorphism]]
<li>[[Classes and Objects]]
<li>[[Narrowing assignments and class tests]]
<li>[[Collections]]
<li>[[Dynamic dispatch]]
<li>[[Enumerations]]
<li>[[Object oriented programming in SOUL]]
<li>[[Intrinsic classes]]
<li>[[Object variables]]
<li>[[Global and session objects]]
<li>[[Structures]]
<li>[[Lists of classes and methods]]
<li>[[Janus SOAP User Language Interface#Other User Language enhancements|"Other User Language enhancements"]]
</ul>
</ul>


[[Category|Overviews]]
[[Category:Overviews]]
[[Category:SOUL object-oriented programming topics]]

Latest revision as of 23:31, 25 July 2017

Background

In broad terms, a class provides descriptions of two things:

  1. The data that describes the state of objects of that class. This state data is in the non-shared public and private variables in the class. For system classes, this data is hidden from SOUL programs, but it is stored more or less identically to SOUL variables.
  2. The operations that can be performed on objects of that class. These operations are the methods (that is, the functions, subroutines, and properties) of the class. For system classes, the methods are in the Model 204 load module and are written in assembler.

When using a class, it is not uncommon to want to perform operations on objects of the class that are not one of the standard methods provided by the class. Before the introduction of enhancement methods, there were three ways of addressing this:

  1. Add a new method to the class.
  2. Create an extension of the class that contains the new method.
  3. Create a shared method in another class that takes objects of the first class as input parameters.

Option 1, above, is impractical in many cases:

  • If the class is a system class, new methods cannot be added using SOUL.
  • If the class belongs to a different group in the organization, it can be problematic getting a new method added.
  • The method being added might need access to private variables in another class, so the method must be added to the other class, not the class to which the method applies.

Option 2 is impractical in many cases:

  • Many system classes, such as collections, intrinsic classes, and XmlNodes are not allowed to be extended.
  • It requires changing variable declarations to use the new class. If the variables, themselves, are parameters, callers might have to be changed to use the new class. This can produce a cascade effect where large chunks of code have to be changed to add an extra method to a class.
  • While inheritance is implemented quite efficiently, there is a cost in code complexity to using inheritance, and using inheritance excessively can cause code to be very difficult to understand.

The most generally usable solution, then, is option 3, a shared method. To illustrate the use of this option, suppose one needs a function that operates on a Stringlist and returns a new Stringlist that contains every other item in the input Stringlist. To accomplish this, you might create a shared method in a class called Utility:

class utility public shared function everyOther(%inlist is object stringlist) - is object stringlist end public shared function everyOther(%inlist is object stringlist) - is object stringlist %i is float %outlist is object stringlist %outlist = new for %i from 1 to %inlist:count by 2 %outlist:add(%inlist(%i)) end for return %outlist end function end class

Then, to invoke this method on, say, the Stringlist object %mylist, you might do something like the following:

%(utility):everyOther(%mylist):print

While there is nothing wrong with this, per se, it blunts some of the benefits of object-oriented syntax. Specifically, while the method is really operating on the %mylist object, the %mylist object appears as a method parameter, rather than the method object. In addition to obscuring the meaning of the statement, it means that the statement must be read "inside out". That is, if the input to EveryOther was, itself, the result of the method invocation, this method invocation would be inside the parentheses, after EveryOther, even though it would actually be invoked before.

For example, if the input to EveryOther was the result of a Sort, the statement might look like:

%(utility):everyOther(%mylist:sortNew('1,20,A')):print

A new type of shared method called an enhancement method has provided a better way of invoking a method on an object of another class. Because enhancement methods do not operate on objects of the containing class, they are considered shared methods of that class, so they are declared inside the Public Shared or Private Shared blocks of the class.

Enhancement method declaration syntax

An enhancement method declaration has the following syntax:

method (class ):name [otherMethodDesc]

Where:

method The method type — Subroutine, Function, or Property. An enhancement method cannot be a Constructor.
class The class of the objects to which the method applies. It cannot be the containing class nor an extension class of the containing class.
name The name of the enhancement method. The name can be the same name as that of other methods in the class, shared or non-shared, as long as it is not the same name as an enhancement method on the same class.
otherMethodDesc Method parameters, method type (for Functions and Properties), and other method qualifiers (like AllowNullObject). Any descriptors available to other methods are available to enhancement methods.

Enhancement method invocation syntax

An enhancement method invocation has the following syntax:

object:(+containerClass)name [(arguments)]

Where:

object An object variable of the class against which the enhancement method operates.
+containerClass The name of the class that contains the enhancement method definition. Note that the class name must be preceded by a plus sign (+) to indicate an enhancement method invocation. The plus sign distinguishes an enhancement method container class name from other uses of class names inside parentheses that might appear in the same context.
name The name of the enhancement method.
arguments Any arguments the enhancement method might take as input. Optional, default, and named parameters work exactly the same way with enhancement methods as with any other methods.

For example, to declare and define an enhancement method version of the EveryOther method described earlier, one would do something like the following:

class utility public shared function (stringlist):everyOther - is object stringlist end public shared function (stringlist):everyOther - is object stringlist %i is float %outList is object stringlist %outlist = new for %i from 1 to %this:count by 2 %outlist:add(%this(%i)) end for return %outlist end function end class

As can be seen in this example, an enhancement method has an implicitly declared object variable called %this. As with unshared methods, the %this variable is a reference to the method object. Unlike unshared methods, the %this variable is not an object of the containing class, but is, instead, an instance of the class to which the enhancement method applies.

This enhancement method could then be invoked as follows:

%mylist:(+utility)everyOther:print

As this example illustrates, the syntax for invoking an enhancement method is more natural from an object-oriented perspective than invoking a shared method that does the same thing.

Using an enhancement method in a chain of methods makes this point even clearer:

%mylist:sortNew('1,20,A'):(+utility)everyOther:print

This chain of methods can be read from left to right rather than from the inside out.

Invoking a Local Alias of an enhancement method

You can also define a Local Alias of an enhancement method, and invoke the enhancement method using the alias:

local alias (stringlist):everyOther for (+utility)everyOther ... %mylist:everyOther:print

This results in a much more natural calling syntax, as though the user-written enhancement method were an integral component of the enhanced class.

Enhancement methods and inheritance

Enhancement methods are never automatically inherited by extension classes of the methods to which they apply, regardless of whether the Inherit keyword is specified on the class declaration for the extension class. For example, the EveryOther method in the preceding section cannot be directly applied to %mylist if %mylist is actually an object of class Funnylist, where FunnyList is an extension of class Stringlist. However, the method can be applied by explicitly specifying the name of the class to which the method applies:

%mylist:(stringlist)(+utility)everyOther:print

In some languages this is referred to as casting, that is, casting a variable as one of its base classes.

You can also explicitly indicate that an extension class is to inherit a base class's enhancement method with a special form of the Inherit statement in the Public or Private Shared blocks:

public shared function (stringlist):everyOther - is object stringlist inherit (funnylist):everyOther from stringlist end public shared

If this were specified, the EveryOther enhancement method could be applied to objects of class Funnylist without any extra qualification.

Note:

Because an enhancement method is not defined in the class to which the method applies, it cannot reference private variables in that class. However, it can reference private members of instances of objects of the containing class.

In fact, one typical application for enhancement methods is to provide a method for creating a new instance of the containing class from an instance of the method object class. These are a special kind of factory method. Such a method might not need to access private members of the method object class, but it might need to access private members of the containing class, so an enhancement factory method is perfectly suitable.

Intrinsic enhancement methods

Just as you can create enhancement methods for other system classes, you can create enhancement methods for the intrinsic classes, specifically for the Float and String classes.

For example, the following definition creates an enhancement method that calculates the length of the hypotenuse of a triangle given the length of one side as the method object and the other side as a parameter:

class calc public shared function (float):hypotenuse(%otherSide is float) - is float end public shared function (float):hypotenuse(%otherSide is float) - is float return ((%this * %this) + - (%otherSide * %otherSide)):squareRoot end function end class

You can invoke the above method as follows:

%hyp = 3:(+calc)hypotenuse(4)

The preceding statement sets %hyp to 5. As can be seen in this example, for a Float enhancement method, the implicitly defined %this variable has a Float datatype.

The following is an example of a String enhancement method that returns the number of vowels in a string:

class myString public shared function (string):vowels is float end public shared function (string):vowels is float %i is float %vowels is float for %i from 1 to %this:length if %this:substring(%i, 1): - positionIn('aeiouAEIOU') then %vowels = %vowels + 1 end if end for return %vowels end function end class

You can invoke the above method as follows:

%nvowels = 'Canberra':(+myString)vowels

The preceding statement sets %nvowels to 3. For a String enhancement method, the implicitly defined %this variable has a Longstring datatype.

Enhancement methods for Collections

Enhancement methods can be added to any class, including Collection classes. Given this capability, it is quite common for a class to have a need to create an enhancement method on a collection of that class.

For example, you might have an Order class that describes an order for widgets. You might want to provide an enhancement method on Arraylist of Order objects that creates a new Arraylist of objects that contains items that need to be back-ordered (there are insufficient widgets in stock to satisfy the order). The method might look something like this:

class order public shared function (arraylist of object order):backorderlist - is collection arraylist of object order end public shared function (arraylist of object order):backorderlist - is collection arraylist of object order %i is float %warehouse is object warehouse global %newlist is collection arraylist of object order %newlist = new for %i from 1 to %this:count if %this(%i):number gt - %warehouse:instock(%this(%i):widgetId) %newlist:add(%this) end if end for return %newlist end function end class

To invoke this enhancement method, one might do something like:

%backOrders = %orders:(+order)backOrderlist

However, because a class has a special relationship to collections of objects of that class, it is not necessary to indicate the class of the enhancement method. That is, you can also write the above method invocation as:

%backOrders = %orders:backOrderlist

It is possible to create enhancement methods with the same name as standard collection methods. For example, it is possible to create an Add method. If you create such a method, the collection class method is completely hidden. That is, there is no way to invoke the collection class method of the same name, even from inside the enhancement method. For this reason, it is generally not a good idea to create an enhancement method for a collection class with the same name as a collection class method.

The ability to hide collection class methods is available mainly to preserve backward compatibility when a new collection class method is introduced. If there were already enhancement methods of the same name, those methods would continue to be invoked.

See also