Michael Coughlan
Page 65
DEPENDING ON NumberOfWords
INDEXED BY WordIdx.
03 DictionaryWord PIC X(20).
03 WordDefinition PIC X(1000).
01 NumberOfWords PIC 9(4) VALUE ZERO.
01 DictionaryName PIC X(20).
METHOD-ID. SetDictionaryName.
LINKAGE SECTION.
01 DictionaryNameIn PIC X(20).
PROCEDURE DIVISION USING DictionaryNameIn.
Begin.
MOVE DictionaryNameIn TO DictionaryName
END METHOD SetDictionaryName.
METHOD-ID. AddWordToDictionary.
LINKAGE SECTION.
01 WordIn PIC X(20).
01 DefinitionIn PIC X(1000).
PROCEDURE DIVISION USING WordIn, DefinitionIn.
Begin.
MOVE FUNCTION UPPER-CASE(WordIn) TO WordIn
SET WordIdx TO 1
SEARCH DictionaryEntry
AT END ADD 1 TO NumberOfWords
MOVE WordIn TO DictionaryWord(NumberOfWords)
MOVE DefinitionIn TO WordDefinition(NumberOfWords)
WHEN WordIn = DictionaryWord(WordIdx)
DISPLAY WordIn " is already in the dictionary"
END-SEARCH
EXIT METHOD.
END METHOD AddWordToDictionary.
METHOD-ID. PrintDictionaryContents.
LOCAL-STORAGE SECTION.
PROCEDURE DIVISION.
Begin.
DISPLAY "Words in - " DictionaryName
PERFORM VARYING WordIdx FROM 1 BY 1 UNTIL WordIdx = NumberOfWords
DISPLAY "Word = " DictionaryWord(WordIdx)
526
Chapter 19 ■ OO-COBOL
END-PERFORM
DISPLAY "------ End of dictionary words --------"
EXIT METHOD.
END METHOD PrintDictionaryContents.
END OBJECT.
END CLASS DictionaryCls.
The first difference between this class program and a normal COBOL program is that there is no IDENTIFICATION
DIVISION. Actually, the IDENTIFICATION DIVISION is now optional. If you are nostalgic, you can still use it. The second difference is that instead of the PROGRAM-ID, you have a CLASS-ID. The CLASS-ID names the class and specifies from what classes it inherits. In this program, the DictionaryCls class inherits from the Base class. The Base class is a system class from which all classes inherit. It corresponds to the class Object in many other OO languages.
The REPOSITORY paragraph allows you to associate internal names with the name of the external file that
contains the code for the class. Internally the dictionary class is known as DictionaryCls, but the system knows it as dictionary.
The next item to consider is the FACTORY. The entries from FACTORY to END FACTORY specify the factory object. The main function of the factory object is to create new object instances where initialization is required. If initialization is not required, the new method inherited from the Base class may be used. For instance, if no initialization was required, you could create new acronym dictionary using the statement
INVOKE DictionaryCls "new" RETURNING AcronymDictionary
In this example, initialization is required, so FACTORY contains a new method that overrides the new method inherited from the Base class. What this new method does is to create a new dictionary object. It does this by using the predefined object identifier SUPER to invoke the new method in the Base class. Once a new dictionary object has been created, it is sent the SetDictionaryName message, and this sets the dictionary name into the dictionary instance object by storing it in a data item declared in the instance object. The factory object could not be used for this purpose because there is only one instance of the factory object (created by including the CLASS entries in the REPOSITORY), and the next time you tried to create a new dictionary, the previous name would be overwritten.
You have probably noticed by now that methods in COBOL bear a very strong resemblance to contained
subprograms (with some minor differences). Instead of a PROGRAM-ID, you use a METHOD-ID; instead of delimiting the scope with END PROGRAM, you use END METHOD; instead of terminating the method using an EXIT PROGRAM statement, you use EXIT METHOD; and instead of WORKING-STORAGE SECTION, you use LOCAL-STORAGE SECTION.
In Listing 19-1-cls, the next item of interest is the entries that define the instance object. These entries start at OBJECT and end at END OBJECT and specify the data and methods of each dictionary instance. This is where the table that holds the dictionary entries is defined; each dictionary instance has a separate table. Defining the table in the WORKING-STORAGE SECTION of the OBJECT keeps it alive for the life of the instance and makes it available to the methods of the object. It is also where DictionaryName is defined.
SetDictionaryName is an internal method. It is only invoked by the new method in the factory. Its only purpose is to take the dictionary name passed as a parameter and move it to less transient storage. It can’t be stored in the method because method storage only persists as long as the method is alive. When the method ends, any data stored in the method is lost.
AddWordToDictionary adds the word and definition passed as parameters to the appropriate place in the table.
PrintDictionaryContents displays the words in the dictionary. The list of words is preceded by the name of the dictionary. The name of the dictionary is obtained from DictionaryName in the WORKING-STORAGE SECTION of the OBJECT.
To keep the class short, so that you are not overwhelmed by detail, I did not include the methods
SearchDictionaryForWord and GetWordDefinition. These are left as an exercise for you.
527
Chapter 19 ■ OO-COBOL
A Formal Introduction to OO-COBOL
Now that you have seen an OO-COBOL program and have an idea about how to create such programs, this section introduces some of the elements of OO-COBOL more formally. But keep in mind that this book is not about OO-COBOL
or object-oriented programming, so I skim over many of the constructs and only stop to deal more thoroughly with those I consider particularly salient.
When you remember all the new syntax that was required for the Report Writer, you may find it amazing that object orientation has been brought to COBOL with so few additions to the language. There is only one new verb (INVOKE), one new data type (OBJECT REFERENCE), and a few new entries such as these:
• CLASS-ID and END CLASS
• REPOSITORY
• FACTORY and END FACTORY
• METHOD-ID and END METHOD
• OBJECT and END OBJECT
• EXIT METHOD
Objects, Classes, and Methods
Before I begin a discussion about creating objects, classes, and methods in COBOL, I should define some of these terms. An object is an encapsulation of data and procedures that operate on that data. In object orientation, the data is known as the object's attributes, and the procedures are known as its methods. For instance, a Stock object might need attributes such a StockId, QtyInStock, ReorderLevel and ReorderQty and might support such methods as GetStockId, AddToStockQty, SubtractFromStockQty, GetStockQty, GetReorderQty, ChangeReorderQty,
GetReorderLevel, and ChangeReorderLevel. Encapsulation means the structure and implementation of the attributes (data) is completely hidden in the object and the only access to the attributes of an object is through the object’s methods. For instance, the only way to change the ReorderLevel of a particular stock item is to invoke that item’s ChangeReorderLevel method.
The user of an object can only discover the value of an attribute or change the value of an attribute by making requests to the object. These requests are known as messages. Each message invokes a method supported by the object. The messages to which an object responds is known as the object interface. Each class actually defines two interfaces: an interface defining the methods supported by the class object (such as the new method) and the interface defining the methods supported by each instance of the class (suc
h as ChangeReorderQty).
A class is a template for creating objects. A class contains all the information you need to create objects of a particular type. In OO-COBOL, a class is called an object factory because it “manufactures” the object instances.
This idea is reinforced by identifying the area of the program where the factory object is defined using the keywords FACTORY and END FACTORY. The factory object may contain its own factory methods and its own factory data. For instance, the Stock class would allow you to create instances of Stock items by sending the new message to the factory object of the Stock class.
In OO-COBOL, a class definition is a program that starts with a CLASS-ID and ends with an END CLASS statement.
The class program may contain its own ENVIRONMENT, DATA, and PROCEDURE DIVISIONS. When you write a class
program, you need to distinguish between three different but related entities:
• The class is the source code program defining the class.
• The factory object is the class at runtime.
• Instance objects are created by the factory object at runtime.
528
Chapter 19 ■ OO-COBOL
Programming with Objects
OO-COBOL is not a fully object-oriented language. This means objects can be used inside a COBOL program that is not itself object oriented. However, whether the program you want to write is an OO-COBOL class or an ordinary procedural COBOL program that uses OO-COBOL objects, the same rules for using the objects apply.
Your program must have a REPOSITORY paragraph. The REPOSITORY lists all the class that the program is going to use. If the program itself is an OO-COBOL class, the REPOSITORY paragraph also lists its superclass (the class from which it is derived).
Your program must declare one or more data items of type OBJECT REFERENCE. An OBJECT REFERENCE data item
holds an object handle. An object handle enables you to send messages to the object. Object references can be moved from one OBJECT REFERENCE data item to another or can be passed as parameters when you INVOKE a method or CALL
a subprogram.
Your program must use the INVOKE verb to send messages to the object. Sending a message to an object invokes the named method in the object. A method is a piece of code that performs one of the functions of the object. Some methods receive or return parameters, so when you invoke a method you may have to include the parameters as part of the message in the INVOKE statement.
Registering a Class
Before you can use an OO-COBOL class, you must register it by declaring it in the REPOSITORY paragraph. Entries in this paragraph link the internal class name with the name of the external file that contains the code for the class.
Registering the class in the REPOSITORY using the CLASS clause creates a data item for each class named, and at runtime this data item holds an object handle to the factory object.
Declaring Object References
When you have declared the class in the REPOSITORY (an action that creates a factory object for the class), you have to declare the data items that will hold the handles of any instance object you may create. To do this, you declare the data items as USAGE OBJECT REFERENCE. For instance, the following data items are declared in Listing 19-1:
01 AcronymDictionary USAGE OBJECT REFERENCE DictionaryCls.
01 NetworkDictionary USAGE OBJECT REFERENCE DictionaryCls.
01 SlangDictionary USAGE OBJECT REFERENCE DictionaryCls.
01 CurrentDictionary USAGE OBJECT REFERENCE.
An object reference can be used
• As the target of an INVOKE statement
• As a parameter to a program or method
• With the SET verb to set one object reference to the value of another or to NULL
• In a comparison comparing one object reference for equality with another or to NULL
The object reference for the factory objects is automatically created when you register a class.
An object reference may be typed or untyped. As demonstrated in Listing 19-1, an untyped object reference data item (called a universal object reference) can hold an object reference for any object. A typed object reference data item can only hold an object reference of the type specified. For instance, the AcronymDictionary data item can only hold a handle (object reference) to a DictionaryCls object instance.
529
Chapter 19 ■ OO-COBOL
Sending Messages to Instance Objects
You interrogate, or change, the values of an object’s attributes by sending messages to the object instance. You send messages to an object instance using the INVOKE verb. When you send a message to an object instance, it causes the method named in the message text to execute. If the method is not found in the object, it is passed up the method inheritance chain until it is recognized and executed. This is how the new and finalize methods that create and destroy object instances are executed. These methods are part of the system provided Base class inherited by every COBOL class.
As you can see from Figure 19-4, the INVOKE verb has a strong similarity to the CALL verb and so requires little in the way of explanation. The ObjectIdentifier is the data item that holds the object reference. MessageLiteral is the name of the method to be invoked. Parameters are passed using the same syntax as the CALL verb except that an additional mechanism (BY VALUE) has been added. The RETURNING phrase allows the invoked method to return a value.
Figure 19-4. Metalanguage for the INVOKE verb
Creating a New Object Instance
Once you have created a data item capable of holding an object reference, you need to create an object instance and store its reference in the data item. You create an object instance by sending a creation message to its factory object (the factory object itself is created when you register it in the REPOSITORY). For objects that do not have any initialization parameters, the creation message is new (see Example 19-2 and Example 19-3). When the new method executes, it allocates the storage required for the object and returns the object handle.
Example 19-2. Registering a Class, Declaring an Object Reference Data Item, and Creating an Object Instance REPOSITORY.
CLASS StockCls AS "stockclassprogram"
: : : : : : : :
WORKING-STORAGE SECTION.
01 StockItem USAGE OBJECT REFERENCE StockCls.
: : : : : : : :
PROCEDURE DIVISION.
: : : : : : : :
INVOKE StockCls "new" RETURNING StockItem
530
Chapter 19 ■ OO-COBOL
Example 19-3. Registering a Class, Declaring an Object Reference Data Item, and Invoking new with an Initialization Parameter to Create a Dictionary Instance
REPOSITORY.
CLASS DictionaryCls AS "dictionary".
: : : : : : : :
WORKING-STORAGE SECTION.
01 AcronymDictionary USAGE OBJECT REFERENCE DictionaryCls.
: : : : : : : :
PROCEDURE DIVISION.
: : : : : : : :
INVOKE DictionaryCls "new" USING BY CONTENT "Acronym Dictionary"
RETURNING AcronymDictionary
Destroying Objects
When you have finished using an object, you must destroy it. This frees the memory it uses. There is no automatic garbage collection in OO-COBOL, so the memory for objects that have been allocated but whose object handles have been lost cannot be recovered. Once an object has been created, it remains in existence until it is destroyed explicitly, even if the data item that holds its object handle is destroyed or the object handle is overwritten.
You destroy an object by sending it the finalize message. Like the new method, finalize is a method provided by the Base class and inherited by all classes. When you finalize an object, the method returns a NULL object reference. Example 19-4 shows how to use INVOKE with the finalize message to destroy an object.
Example 19-4. Using finalize to Destroy an Object
INVOKE AcronymDictionary "finalize" RETU
RNING AcronymDictionary
Predefined Object Identifiers
I mentioned the NULL object reference in the previous section. NULL is one of three predefined object identifiers.
The identifiers and their significance are given in Table 19-2.
531
Chapter 19 ■ OO-COBOL
Table 19-2. Predefined Object Identifiers
Predefined Object Identifier
Meaning
NULL
The predefined object reference NULL contains the null object-reference value that
is a unique value guaranteed by the implementer never to reference an object.
It represents a value used to indicate that data items defined as USAGE OBJECT
REFERENCE do not contain a valid address. NULL must not be specified as a receiving
operand, but it can be used in a comparison such as
IF AcronymDictionary = NULL
DISPLAY "The acronym dictionary object does not exist"
END-IF
SELF
SELF is a predefined object identifier used in the PROCEDURE DIVISION of a method.
SELF refers to the object instance used to invoke the currently executing method.
By using SELF you can cause an object to send a message to itself. This is useful if
you want a method to invoke one of its siblings. For instance, in Listing 19-1-cls
you could use SELF to invoke the SetDictionaryName method from one of the
other methods using a statement such as
INVOKE SELF "SetDictionaryName"
USING BY CONTENT NewDictionaryName
You might want to use SELF because you have placed a piece of code that is used by
several different methods in a method on its own and want to use this method like
a subroutine.
SUPER
SUPER allows an object to send a message to itself, but the method invoked is a not a
method in the class itself but rather a method in one of the superclasses of the class.
If SUPER is used from an instance method, the system searches its way up through