This chapter explains the rules for writing classes, with short examples of code. For full syntax definitions, see your Language Reference.
You need to write OO COBOL classes to create objects other than the ones in the class libraries supplied by Micro Focus. When you write a class, you need to understand three distinct entities:
The source code defining the class.
The class at run time.
Created by the factory object at run time.
A class embodies all the behavior for its factory object and its instance objects. It defines the factory and instance data, and factory and instance methods. A class also inherits the data and methods of its superclass.
Methods are discussed both in this chapter, and in the chapter Methods.
Each class describes the behavior of two different objects:
There is never more than one occurrence of each type of factory object in an application, whereas there can be many occurrences of its instances. The main function of any factory object is to create instances, although in some cases a factory object has other behavior as well.
Micro Focus AlternativeIn Micro Focus OO COBOL the class object is equivalent to the factory object.
A class is structured as a set of nested source elements (see Figure 4-1).
The code block below shows the outline of an example class. An ellipsis (...) shows where code has been omitted, and indentation indicates the levels of nesting.
class-id. Example inherits *> Class identification. from Base. environment division. *> Environment division *> header is optional. *> All sections legal in *> MF or ANSI 85 COBOL *> Environment Division *> are valid, though not *> shown here. ... configuration section. repository. *> Repository paragraph *> names the classes this *> class refers to. class Example as "exmp" *> Information for the *> Example class is stored in *> the external repository *> under the name "exmp". class Base as "base" *> Information for the *> Base class is stored in *> the external repository *> under the name "base" class CharacterArray . *> Period terminates paragraph. ...
factory. *> Defines the start of the *> factory object. environment division. data division. working-storage section. *> Defines factory object data ... ... method-id. "new". *> Start factory method "newWithData". ... end method "new". *> End factory method "newWithData". end-factory. *> End of the factory object object. *> Start of the code *> defining behavior *> of instances *> of the class. working-storage section. *> Defines instance data. ... method-id. "sayHello".*> Start of instance *> method "sayHello" ... end method "sayHello".*> End of instance method. end object. *> End of code for *> instances. end class Example.
To see a class outline example that uses the Micro Focus COBOL alternative syntax, see the section Summary of Syntax Alternatives in the chapter Micro Focus OO COBOL Alternative Syntax.
Most OO COBOL classes inherit from a superclass. In the Micro Focus class libraries the only exception to this is the Base class, which is at the root of the inheritance tree for all other classes in the class libraries. When you write your own COBOL classes you will nearly always write them as a subclass either of Base or of another class.
In addition to a subclass inheriting all the methods of the classes above it in the hierarchy, it can also inherit directly from more than one class; this is known as multiple inheritance.
If you do decide to write a class which is not a subclass either of Base or of one of its subclasses, you will not inherit the functions provided by Base. You will have to implement your own method for destroying unwanted instances, to replace the "finalize" method in the Base class. You don't need to implement your own "new " method for creating new instances, because this method is part of the Behavior class, and is always available. Behavior is provided in the Base Class Library, and can be thought of as a metaclass (that is, a class that describes the underlying behavior of all objects).
There are two things a class can inherit from its superclass or superclasses:
These are dealt with separately in the next two sections.
A factory object inherits all the factory methods of its superclass, and an instance inherits all the instance methods of its superclass. Since the superclass in turn inherits all the factory and instance methods of its superclass, it follows that an object inherits all the methods implemented by superclasses all the way up to the root class (usually Base). When a method is implemented in more than one class in an heirarchy, then when an object of a class that inherits the method is executed, it is the method that is lower down in the hierarchy that is executed.
If a class inherits from a class that also inherits from a class, and if a method of the same name exists in both superclasses, then when that method is invoked, the method in the superclass immediately above the subclass is called.
If a class inherits directly from more than one class (multiple inheritance), and if a method of the same name exists in more than one of those superclasses, then when that method is invoked, the method in the superclass declared first in the Class-ID paragraph of the subclass is called.
Figure 4-2 shows an object C1 receiving the "setValue" message. Object C1 is an instance of class C. The superclass of class C is class B, and the superclass of class B is class A. Classes B and C do not implement instance method "setValue", so the message gets passed all the way up to class A where the code for "setValue" exists.
Instance method "getValue" is implemented in both class B and class A. If object C1 was sent the "getValue" message, it would use the implementation in class B, as this would be found first. You can always reimplement a method that is already in a superclass when you want to change its behavior for subclasses.
Figure 4-2: Method Inheritance
Figure 4-3 shows a case where an instance object gets sent a message not implemented in any of its superclasses. The message gets passed up the inheritance chain until it gets to Base. If Base does not understand the message either, it sends the message "doesNotUnderstand" back to the instance which originally received the message.
Figure 4-3: Trapping Messages That are Not Understood
The "doesNotUnderstand" message then gets passed up the chain until it reaches Base, where the "doesNotUnderstand" method raises an exception which stops the application. If you want to create objects which can handle messages they don't understand, you can reimplement the "doesNotUnderstand" message to trap the error before it gets back up to the Base class.
Figure 4-4 shows what happens when you send a message to a factory object that does not understand it. The message is passed to the factory methods for all the superclasses in the chain. When it reaches Base, something slightly different happens.
Figure 4-4: Trapping a Message Not Understood by a Factory Object
All factory objects are instances of Behavior. Behavior is part of the Base Class Library, and can be thought of as a metaclass, or class that describes behavior common to all classes of object. If the message is an instance method of Behavior, it is executed. Otherwise, Behavior sends it to the instance methods of Base. It does this because Behavior is a subclass of Base.
If no method is found, then Base sends the "doesNotUnderstand" message back to the object which originally received the message.
A factory object inherits all the factory data of its superclass, and an instance object inherits all the instance data of its superclass. The inherited data of a subclass includes any data that the superclass itself inherited from a class higher up the hierarchy. The inherited object data is initialized when an object is created. The inherited factory data is allocated independently from the factory data of the inherited class or classes when the factory of the subclass is created. A subclass can only access inherited data through the methods it inherited from the same class. For example, if the class Manager inherits from the class Employee, and Employee defines the data item:
employeeNumber PIC 999
The Manager class cannot store a value in its inherited employeeNumber data item by using a statement such as:
move 100 to employeeNumber
Instead it must use a method declared in the Employee class, for example:
invoke self "setEmployeeNumber" using 100
The object reference SELF is a reserved name which always refers to the object in which it occurs (see the section Predefined Object Reference Names in the chapter Methods).
Micro Focus COBOL provides alternative syntax and behavior; see the section Data Inheritance in the chapter Micro Focus OO COBOL Alternative Syntax.
OO COBOL supports full COBOL file handling syntax, and you can use this in your classes to read and write to COBOL file types. To do file handling, you need to include the File Section and a File-Control paragraph for each file in your factory code or instance code. One or more of your factory methods or instance methods will contain file processing verbs such as OPEN, CLOSE, READ.
When the factory or instance source elements of a class contains a file specification, any subclasses below it in the class hierarchy inherit the file specification. Each factory or instance object of each of these subclasses has its own file connector unless the EXTERNAL clause is specified for the file in the original class. You can use dynamic filename assignment or file sharing to resolve conflicts in accessing the physical files associated with these file connectors. For more infomation about file connectors, dynamic filename assignment and file sharing see your Language Reference.
The part of the class that defines factory object behavior is the factory. The factory source element is nested inside a COBOL class source element, bracketed by the FACTORY and END-FACTORY headers. The factory source element embodies the behavior of the factory object, which exists only at run time when the class is loaded.
Not all classes contain a factory source element; the main function of a factory object at run time is to create new instances. This function is often inherited from the superclass. The main reasons for writing your own factory object are:
To see where the factory source element fits within the class source element, review the code for an example class in the section Class Structure.
You declare factory object data in the Working-Storage Section of the factory source element. Factory object data can be accessed only from factory methods and can optionally be inherited by a subclass.
When a class is loaded by an application, the run-time system allocates an area of memory for the data declared in the Working-Storage Section of the factory source element, and for data declared in any superclasses it inherits from.
A factory object can only access data directly that it has declared itself. A factory object can only access data it has inherited by sending messages to its superclass.
For further information about data inheritance using ISO 2002 OO COBOL see the section Data Inheritance. For further information about data inheritance using Micro Focus alternative syntax see the section Data Inheritance in the chapter Micro Focus OO COBOL Alternative Syntax.
Class initialization code is executed when the class is loaded by the run-time system; it is only ever executed once during an application run. Class initialization code is optional, and for most classes is not required. You use it for initializing factory object data. For example, you might want to make certain objects available to your factory object at startup. You couldn't specify these in value clauses in your factory Working-Storage Section because value clauses can only specify static data, whereas object handles are allocated dynamically at run time.
To write class initialization code, write a factory method called "initializeClass", and put all the initialization code in that method. The method will be automatically called by the run-time system when it loads the class.
Your COBOL system uses demand loading to reduce application startup times. This means you can't predict exactly when any particular class will be loaded. A class is guaranteed to be loaded and initialized by the time it receives its first message. Because you do not know exactly when a class is loaded, you should not rely on class initialization code to set values in external variables used elsewhere in the application.
Factory methods are source elements nested inside the factory object source element. There is only ever one occurrence of any particular type of factory object when you run an application, so factory methods are generally concerned with the creation and management of instance objects.
Factory methods have access to factory object data.
You code factory and instance methods in the same way; the only difference between them is their position in the class source element and the scope of data to which they have access. Coding methods is explained in more detail in the chapter Methods.
The part of the class that defines instance object behavior is the object. The object source element is nested inside a COBOL class source element, bracketed by the OBJECT and END-OBJECT headers. It follows the factory source element. The object source element embodies the behavior of the instance objects, which exist only at run time.
To see where the object source element fits within the class source element, review the code for an example class in the section Class Structure.
You declare instance data in the Working-Storage Section of the instance object source element. Instance data can only be accessed from instance methods, and each instance can only see its own instance data. An instance object inherits all the instance data of its superclass and any classes higher up the hierarchy.
When an instance is created by an application, the run-time system allocates an area of memory for the data declared in the Working-Storage Section, and for instance data declared in any superclasses it inherits from. For more information about creating instances see the section Instance Creation Methods in the chapter Methods.
An instance object can only access data directly that it has declared itself. The only way an instance object can access data it has inherited from a superclass, is to send a message to the superclass. For further information about data inheritance using ISO 2002 OO COBOL see the section Data Inheritance. For further information about data inheritance using Micro Focus alternative syntax see the section Data Inheritance in the chapter Micro Focus OO COBOL Alternative Syntax.
You should only declare as instance data the data which is actually part of an object's attributes. You should declare data items which you need for temporary working and calculations elsewhere. The safest place to declare temporary data is in the Local-Storage Section of any methods which require it.
For example, the only instance data declared for the Account objects used by the tutorials in this book is the customer name, account number and balance. If there is a piece of data which is the same for all instances (for example, the interest rate), it is declared as factory object data. All temporary data is declared in method Local-Storage Sections as required.
Every byte of storage you declare as instance data is allocated every time your application creates a new instance object. Only declaring as instance data the data which is strictly part of the object's attributes keeps memory overhead to a minimum.
How you code the initialization for an object is largely up to you. There is no explicit mechanism for instance initialization in the way there is for class initialization. The easiest and most readable way is to code an "initialize" method, and to write an instance creation method (which is always a factory method) which invokes the "initialize" method after it has created an instance. You can also initialize data in the Working-Storage Section using VALUE clauses.
Some objects might be initialized from data supplied with the message that creates them. The instance creation method would have to pass these parameters to the "initialize" method. Other types of object are always initialized to the same state at creation. Windows and dialog boxes usually fall into this category; the initialization code usually consists of painting them with the same set of gadgets and labels every time.
Instance methods are source elements nested inside the instance object source element. Instance methods are the way in which an application can alter the state of an object or query its attributes.
Instance methods have access to instance data.
You code factory and instance methods in the same way; the only difference between them is their position in the class source element and the scope of data to which they have access. How to code methods is explained in more detail in the chapter Methods.
A parameterized class is a skeleton class that has one or more formal parameters. When you provide specific class names or interface names as actual parameters, the Compiler expands the parameterized class into a new non-parameterized class.
The purpose of parameterized classes is to make it easier for you to create many similar classes. For example, if you need four linked list classes to enable you to store four different types of object, you can write one parameterized class, then use the expansion feature to create the four linked list classes at compile time. This is less work than writing four complete linked list classes.
(In a linked list each item has a link to the next item. Linked lists provide an easy way of storing and retrieving objects.)
An additional advantage of parameterized classes is that they enable conformance checking at compile time. If you have a general linked list that can store any type of object, an object of a different class to the class your application expects could be added to the table; this could result in unexpected run-time results. With an expanded parameterized class, any INVOKE statement trying to add an object of the wrong class will be caught at compile time by the Compiler.
A parameterized class always has a USING phrase that lists the formal parameters in its Class-ID paragraph.
You need to specify the EXPANDS phrase in the entry for the class in the Repository paragraph of any program that uses a class created from a parameterized class.
For example, suppose that you wanted to create a linked list for employees who report to a particular manager. The parameterized class could look like this:
$set repository(update on checking on) class-id. PLinkList as "PLinkList" inherits Base using fparam1. repository. class fparam1 class Base as "base" . factory. * new: creates new linked list, initialise * head and tail pointers method-id. "new". linkage section. 01 newObject usage object reference PLinkList. procedure division returning newObject. invoke super "new" returning newObject invoke newObject "initlinkedlist" exit method end method "new". end factory. object. working-storage section. * data items to hold head of linked list and position * during iteration method-id. "initLinkedList". local-storage section. 01 tmp-element usage object reference fparam1. procedure division. * code to initialize linked list exit method end method "initLinkedList". method-id. "add". 01 newEntry usage object reference LinkEntry. linkage section. 01 lk-element usage object reference fparam1. procedure division using by value lk-element. * code to add the element to the linked list exit method. end method "add". method-id. "getFirst". local-storage section. 01 tmp-element usage object reference. linkage section. 01 lk-element usage object reference fparam1. procedure division returning lk-element. * code to get the first element from a linked list exit method. end method "getFirst". method-id. "getNext". local-storage section. 01 tmp-element usage object reference. linkage section. 01 lk-element usage object reference fparam1. procedure division returning lk-element. * code to get the next element from a linked list exit method. end method "getNext". end object. end class PLinkList.
At compile time all occurrences of fparam1 are replaced by the the name of an actual class or interface. To make this happen you need to specify the ACTUAL-PARAMS Compiler directive. In this example the directive is:
ACTUAL-PARAMS(ELinklist Employee)
Elinklist is the new class to create from the parameterized class. Employee is the class name that replaces fparam1.
You can now use the ELinklist class in a program, for example:
$set repository(update on checking on) class-id. Manager as "Manager" inherits Employee. repository. class Employee as "Employee" class Manager as "Manager" class PLinkList as "PLinkList" class ELinkList as "ELinkList" expands PLinkList using Employee. factory. ... end factory. object. working-storage section. 01 ListEmployees usage object reference ELinkList value null. * initManager: initalize linked list method-id. "initManager". procedure division. set ListEmployees to ELinkList::"new" exit method end method "initManager". * AddEmployee: add employee to linked list method-id. "addEmployee". linkage section. 01 lk-employee usage object reference Employee. procedure division using by value lk-employee. invoke ListEmployees "add" using by value lk-employee exit method end method "addEmployee". * display : display manager's details and all reporting * employees from linked list method-id. "display" override. local-storage section. 01 employee usage object reference Employee. procedure division. ... invoke ListEmployees "getfirst" returning employee perform until exit if employee not = null invoke employee "display" else exit perform end-if invoke ListEmployees "getnext" returning employee end-perform ... exit method. end method "display".
Copyright © 2004 Micro Focus International Limited. All rights reserved.