Creating Your Own Classes Using Directives

By analyzing your problem in terms of objects, you can determine what classes need to be created. You can create a class using messages or directives. Directives are a new kind of Rexx clause, and they are preferred over messages because the code is easier to read and understand, especially in large programs. They also provide an easy way for you to your class definitions with others using the PUBLIC option.

What Are Directives?

A Rexx program is made up of one or more executable units. Directives separate these units, which themselves are Rexx programs. Rexx processes all directives first to set up any classes, methods, or routines needed by the program. Then it runs any code that exists before the first directive. The first directive in a program marks the end of the executable part of the program. A directive is a kind of clause that begins with a double-colon (::) and is non-executable (a directive cannot appear in the expression of an INTERPRET instruction, for example).

The Directives Rexx Provides

The following is a short summary of all the Rexx directives. See the Open Object Rexx: Reference for more details on, or examples of, any of these Rexx directives.

The ::CLASS Directive

You use the ::CLASS directive to create a class. Programs can then use the new class by specifying it as a Rexx environment symbol (the class name preceded by a period) in the program. For example, in A Sample Program Using Directives, the Savings class is created using the ::CLASS directive. A program can then use the new class by specifying it as an environment symbol, ".savings".

The new class that you create acquires any methods defined by subsequent ::METHOD directives within the program, until either another ::CLASS directive or the end of the program is reached.

You can use the ::CLASS directive's SUBCLASS option to make the new class the subclass of another. In A Sample Program Using Directives, the Savings class is made a subclass of the Account class. A subclass inherits instance and class methods from its specified superclass; in the sample, Savings inherits from Account.

Additional ::CLASS directive options are available for:

  • Inheriting instance methods from a specified metaclass as class methods of the new class (the METACLASS option). For more information on metaclasses, see Metaclasses.

  • Making the new class available to programs outside its containing Rexx program (the PUBLIC option). The outside program must refer to the new class by using a ::REQUIRES directive.

  • Subclassing the new class to a mixin class in order to inherit its instance and class methods (the MIXINCLASS option).

  • Adding the instance and class methods of a mixin class to the new class, without subclassing it (the INHERIT option).

When you create a new class, it is always a subclass of an existing class. If you do not specify the SUBCLASS or MIXINCLASS option on the ::CLASS directive, the superclass for the new class is the Object class.

Your class definition can be in a file of its own, with no executable code preceding it. For example, when you define classes and methods to be shared by several programs, you put the executable code in another file and refer to the class file using a ::REQUIRES directive.

Rexx processes ::CLASS directives in the order in which they appear, unless there is a dependency on some later directive's processing. You cannot create two classes that have the same class name in one program. If several programs contain classes with the same name, the last ::CLASS directive processed is used.

The ::METHOD Directive

The ::CLASS directive is usually followed by a ::METHOD directive, which is used to create a method for that class and define the method's attributes. The next directive in the program, or the end of the program, ends the method.

Some classes you define have an INIT method. INIT is called whenever a NEW message is sent to a class. The INIT method must contain whatever code is needed to initialize the object.

The ::METHOD directive can be used for:

  • Creating a class method for the most-recent ::CLASS directive (the CLASS option).

  • Creating a private method; that is, a method that works like a subroutine and can only be activated by the objects of the same type it belongs to—otherwise the method is public by default, and any sender can activate it.

  • Creating a method that can be called while other methods are active on the same object, as described in Activating Methods (the UNGUARDED option).

The ::ATTRIBUTE Directive

A ::CLASS directive can also be followed by ::ATTRIBUTE directives, which are used to create methods that directly access internal attributes of an object. For example, the Account class could define

::attribute balance

Which would allow the account balance to be set or retrieved.

  anAccount~balance = 10000     -- set a new account balance
  say anAccount~balance         -- display the current account balance

The access methods can created as read-only, or given private scope.

The ::ROUTINE Directive

You use the ::ROUTINE directive to create a named routine within a program. The ::ROUTINE directive starts the named routine and another directive (or the end of the program) ends the routine.

The ::ROUTINE directive is useful organizing functions that are not specific to a particular class type.

The ::ROUTINE directive includes a PUBLIC option for making the routine available to programs outside its containing Rexx program. The outside program must reference the routine by using a ::REQUIRES directive.

The ::REQUIRES Directive

You use the ::REQUIRES directive when a program needs access to the classes and objects of another program. This directive has the following form:

::REQUIRES program_name

::REQUIRES directives are processed before other directives and the order of the ::REQUIRES directives determines the search order for the classes and routines defined in the named programs.

Local routine or class definitions within a program override routines or classes of imported through ::REQUIRES directives.

How Directives Are Processed

You place a directive (and its method code) after the program code. When you run a program containing directives, Rexx:

  1. Processes the directives first, to set up the program's classes, methods, and routines.

  2. Runs any program code preceding the first directive. This code can use any classes, methods, and routines set up by the directives.

    Once Rexx has processed the code preceding the directive, any public classes and objects the program defines are available to programs having the appropriate ::REQUIRES directive.

A Sample Program Using Directives

Here is a program that uses directives to create new classes and methods:

asav = .savings~new              /* executable code begins */
say asav~type                    /* executable code        */
asav~name= "John Smith"          /* executable code ends   */

::class Account                  /* directives begin ...   */

  ::method type
    return "an account"

  ::attribute name

::class Savings subclass Account

  ::method type
    return "a savings account"   /* ... directives end     */

The preceding program uses the ::CLASS directive to create two classes, the Account class and its Savings subclass. In the ::class Account expression, the ::CLASS directive precedes the name of the new class, Account.

The example program also uses the ::METHOD directive to create a TYPE method and ::ATTRIBUTE to create NAME and NAME= methods for Account. In the ::method type instruction, the ::METHOD directive precedes the method name, and is immediately followed by the code for the method. Methods for any new class follow its ::CLASS directive in the program, and precede the next ::CLASS directive.

In the ::attribute name directive, we're creating a pair of methods. The NAME method returns the current value of the NAME object variable. The NAME= method can assign a new value to the NAME object variable.

You do not have to associate object variables with a specific object. Rexx keeps track of object variables for you. Whenever you send a message to savings account Asav, which points to the Name object, Rexx knows what internal object value to use. If you assign another value to Asav (such as "Mary Smith"), Rexx recovers the object that was associated with Asav ("John Smith") as part of its normal garbage-collection operations.

In the Savings subclass, a second TYPE method is created that supersedes the TYPE method Savings would otherwise have inherited from Account. Note that the directives appear after the program code.

Another Sample Program

A directive is nonexecutable code that begins with a double colon (::) and follows the program code. The ::CLASS directive creates a class; in this example, the Dinosaur class. The sample provides two methods for the Dinosaur class, INIT and DIET. These are added to the Dinosaur class using the ::METHOD directives. After the line containing the ::METHOD directive, the code for the method is specified. Methods are ended either by the start of the next directive or by the end of the program.

Because directives must follow the executable code in your program, you put that code first. In this case, the executable code creates a new dinosaur, Dino, that is an instance of the Dinosaur class. Rexx then runs the INIT method. Rexx runs any INIT method automatically whenever the NEW message is received. Here the INIT method is used to identify the type of dinosaur. Then the program runs the DIET method to determine whether the dinosaur eats meat or vegetables. Rexx saves the information returned by INIT and DIET as variables in the Dino object.

In the example, the Dinosaur class and its two methods are defined following the executable program code:

dino=.dinosaur~new         /* Create a new dinosaur instance and
                                            /* initialize variables */
dino~diet                  /* Run the DIET method          */
exit
 
::class Dinosaur           /* Create the Dinosaur class  */

  ::method init            /* Create the INIT method     */
    expose type
    say "Enter a type of dinosaur."
    pull type
    return

  ::method diet            /* Create the DIET method     */
    expose type
    select
    when type="T-REX" then string="Meat-eater"
    when type="TYRANNOSAUR" then string="Meat-eater"
    when type="TYRANNOSAURUS REX" then string="Meat-eater"
    when type="DILOPHOSAUR" then string="Meat-eater"
    when type="VELICORAPTOR" then string="Meat-eater"
    when type="RAPTOR" then string="Meat-eater"
    when type="ALLOSAUR" then string="Meat-eater"
    when type="BRONTOSAUR" then string="Plant-eater"
    when type="BRACHIOSAUR" then string="Plant-eater"
    when type="STEGOSAUR" then string="Plant-eater"
    otherwise string="Type of dinosaur or diet unknown"
    end
    say string
    return 0