SOM Collection Classes: A Primer for the VisualAge COBOL Programmer
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
The System Object Model (SOM) provides a rich set of collection classes for SOM-enabled applications to use. SOM's collection classes come in many forms, including linked lists, sets, ordered collections, queues, and dictionaries. While samples and documentation exist for using these collection classes in C++, little is currently available to guide the programmer who uses object-oriented COBOL.
The first part of this article introduces the concept of SOM collection classes, including the various collection classes, iterator classes, and associated abstract classes. The second part illustrates the programming techniques for implementing collection classes through a simple application programmed in object-oriented COBOL with IBM's VisualAge for COBOL for OS/2.
- 1 First Part
- 2 Why Use SOM Collection Classes in an Application?
- 3 Types of Collections
- 4 Abstract Base Classes
- 5 Iterators
- 6 Mixin Classes
- 7 Inheritance Hierarchy of SOM's Collection Classes
- 8 Methods
- 9 Implementing SOM Collection Classes in an Application
- 10 An Illustration
- 11 Second Part
- 12 Putting It All Together
- 13 Collection Classes: Important in Object-Oriented Programming
Using collections is a common practice in object-oriented programming. Collection classes are provided for your convenience. They implement most of the common data structures you encounter in programming, relieving you from the task of coding them.
Collection classes are a set of classes whose purpose is to contain other objects. In general, a collection may be thought of as an abstract data type of a set of objects that you want to manipulate as a group. A collection is not an array with elements, but a much more sophisticated mechanism for storing and managing objects.
When an element of a collection is referenced, that element is an object, complete with its methods and attributes, not merely data. The collection is considered an object, and it provides various methods for manipulating its elements, as well as itself.
The System Object Model (SOM) collection classes were developed by Taligent, a wholly-owned IBM subsidiary. Taligent's systems software is based entirely on object-oriented technology. The company's philosophy, which is to provide open environments that can be extended by software developers, is evident in the structure of the SOM collection classes.
Why Use SOM Collection Classes in an Application?
If collections are implemented in SOM-enabled classes, they can be used unaltered in applications coded in other programming languages supporting SOM, and their reuse by other applications (OO COBOL or otherwise) is facilitated. SOM gives you efficient and reliable implementations of the common abstract data types used in collections, plus it furnishes a framework of properties to guide you in determining the best type of collection to use in your application.
Additionally, collections are unbounded in the number of elements they may contain (at least, any limitations are beyond the practical ones), whereas tables defined in an application program are fixed in size and can require extensive program changes and recompilation when they must be expanded.
Types of Collections
Depending on their type, collections have differing internal structures and differing access methods for manipulation of their elements. The types of collections can be classified into various categories based on the following properties: ordering, keyed access, and uniqueness of entries. Ordering means that the elements of the collection may be unordered, sorted, or sequential. Keyed access implies a key is used to reference the objects in a collection. Uniqueness of entries dictates whether or not the collection may contain duplicate objects.
IBM's System Object Model (SOM) V2.1 implements the following main types of collections:
- Hash Table - A collection consisting of (key, value) pairs. The key provides the means for mapping into the collection (or table), and the value is the data element stored into the collection (or table). Hash tables provide fast lookup of a value when given its associated key. Hash tables do not permit two (key, value) pairs to have the same key. Two unique pairs can hash to the same table, but the instantiation of each must be unique. SOM's associated class for this data structure is somf_THashTable.
- Dictionary - An unordered collection with (key, value) pairs. Equal (key, value) pairs can occur only once. Methods for retrieving a key given its value are provided, but these may be slow. This data structure is implemented in SOM with the somf_TDictionary class.
- Set - An unordered collection of unique objects. If duplicate objects are required, consider using a deque (see the next item) instead. SOM uses somf_TSet to implement sets.
- Deque - A queue, stack, or deque collection. It is based on the order in which objects are added to, or removed from, the collection. It can be used as a queue or a stack. A queue is a list in which elements are inserted, then retrieved via a first-in, first-out (FIFO) approach. A stack is a list in which elements are inserted, then retrieved with a last-in, first-out (LIFO) approach. A deque is a double-ended queue (hence, its name) that allows insertion and retrieval from either end of the list. Duplicate entries are allowed, and the only ordering of the structure is determined by how elements are inserted into it. Objects can be inserted and removed from any point in the collection. It is the most flexible of the collection classes provided by SOM and is implemented with the somf_TDeque class.
- Primitive Linked List - A collection in which each element is linked to the element before it and after it. Duplicates are not allowed. The elements of the collection may be traversed using the links between elements. SOM uses somf_TPrimitiveLinkedList to define these classes.
- Sorted Sequence - A collection in which the order of its elements is determined by how the elements relate to each other, ranging from largest to smallest. SOM uses the somf_TSortedSequence class to implement sorted sequences.
- Priority Queue - A special case of the sorted sequence. It keeps the objects of a collection ordered, based on some ordering function. It differs from a queue in that a new element may be inserted and then, say, the largest or smallest deleted (as opposed to the oldest in a straight FIFO queue). SOM uses somf_TPriorityQueue to implement priority queues.
Abstract Base Classes
An abstract base class describes general characteristics and cannot be instantiated. Such classes also include pure virtual functions that must be overridden by classes derived from the abstract base classes.
SOM collection classes include the following abstract base classes:
- somf_TCollection - Represents a group of objects.
- somf_TIterator - Declares the characteristics common to all iterator classes.
- somf_TSequence - Declares the characteristics common to all collections with ordered elements.
- somf_TSequenceIterator - Declares the characteristics of iterators of collections with ordered elements.
Methods declared in the abstract base classes must be overridden by classes inheriting from them. In some cases, the collection classes and iterator classes provide the override methods; in other cases, you must provide them.
An iterator for a particular collection allows iteration over each object contained in the collection. For those conversant with relational databases, it is analogous to a cursor.
Some readers may wonder why iterators are separate and not included in the base collection classes. From an architectural standpoint, this separation is advantageous, because it allows multiple iterators to be defined for a given collection. If iterators were included with the base collection classes, each instantiation of a collection would be limited to a single iterator.
Iterators return objects of the class that is contained in the collection. Note that somf_TDictionaryIterator and somf_THashTableIterator will return objects of the type somf_TAssoc, not simply objects of the somf_MCollectible class. somf_TAssoc is used to hold a (key, value) pair and is a supporting class that inherits from somf_MCollectible.
If a collection changes while an iterator is in use, the iterator is no longer valid. That is, if an iterator is being used to iterate through a collection, and an additional element is added to the collection, the iterator cannot be used to access the remaining elements of the collection. The iterator has to be reset, and iteration has to start over.
If a collection is ordered, the iterator returns the elements of the collection in the correct order. If a collection is unordered, the iterator returns the elements in a random order. Note that iterators are themselves objects, with their own set of methods, and must be instantiated prior to use.
SOM provides the following iterator classes, each of which is associated with one of the main collection classes:
- somf_THashTableIterator - Used to iterate over somf_THashTable collections.
- somf_TDictionaryIterator - Used to iterate over somf_TDictionary collections.
- somf_TSetIterator - Used to iterate over somf_TSet collections.
- somf_TDequeIterator - Used to iterate over somf_TDeque collections.
- somf_TPrimitiveLinkedListIterator - Used to iterate over somf_TPrimitiveLinkedList collections.
- somf_TSortedSequenceIterator - Used to iterate over somf_TSortedSequence collections.
- somf_TPriorityQueueIterator - Used to iterate over somf_TPriorityQueue collections.
Mixin classes are "mixed in" with other classes to produce new classes. For an object to be eligible for use in a collection, it must inherit from a mixin class. The mixin class declares certain characteristics for the element that the collection class requires in order to process the element. Multiple inheritance allows you to inherit from multiple mixin classes to create specialized collectible classes.
SOM's mixin classes used by the main collection classes are:
- somf_MCollectible - Defines the general characteristics of objects inserted into any of the collection classes.
- somf_MLinkable - Defines the general characteristics of objects containing links.
- somf_MOrderableCollectible - Defines the general characteristics of objects placed in ordered collections.
Figure 1 shows which of the mixin classes must be inherited by an object being placed into each of the main collection classes.
|Main Collection Class||Mixin Class for Collected Object|
Inheritance Hierarchy of SOM's Collection Classes
Figure 2 illustrates the hierarchy of the collection and iterator classes.
SOMObject somf_MCollectible somf_MOrderableCollectible somf_TAssoc somf_TCollection somf_TSet somf_TDictionary somf_TPriorityQueue somf_TSequence somf_TDeque somf_TSortedSequence somf_THashTable somf_TIterator somf_THashTableIterator somf_TSetIterator somf_TDicitionaryIterator somf_TPriorityQueueIterator somf_TSequenceIterator somf_TDequeIterator somf_TSortedSequenceIterator somf_MLinkable somf_TDequeLinkable somf_TPrimitiveLinkedList somf_TPrimitiveLinkedListIterator
Figure 2. Hierarchy of Collection and Iterator Classes
In general, the method names are a good indicator of the functions they perform. Many classes have specialized methods. In this article, only the commonly used methods are listed. For more information, particularly regarding method parameters, consult the SOMobjects Developer Toolkit Collection Classes Reference Manual (part number 59G5230 - also available in soft copy with the SOMobjects Developer Toolkit 2.1, part number 10H9877). As discussed in the previous section on abstract classes, methods defined in the abstract classes must be overridden in derived classes before they can be used.
For classes derived from the somf_MCollectible mixin class, you must provide a somfIsEqual method. This makes sense, because SOM cannot know what your application will consider as object equality.
In classes derived from somf_OrderableCollectible, you must provide (in addition to a somfIsEqual method) somfIsGreaterThan and somfIsLessThan methods in order to use the somfCompare method. Again, this makes sense, because SOM cannot know how to evaluate the relationships between your application objects.
For the iterator classes, somfFirst and somfNext methods are furnished. somfFirst returns the first element of a collection, and somfNext returns the subsequent element. As noted earlier, iterators of dictionaries and hash tables will return objects of the type somf_TAssoc. Once you have retrieved a somf_TAssoc, you can use somfGetKey and somfGetValue to get the attributes associated with the (key, value) pair.
Iterators of the types somf_TSortedSequenceIterator, somf_TDequeIterator, and somf_TPrimitiveLinkedListIterators supply somfLast and somfPrevious methods, in addition to the somfFirst and somfNext methods common to all iterator classes.
To create an iterator of the type somf_THashTableIterator, you must use the somfTHashTableIteratorInit method of the somf_THashTableIterator class. This is also true for the somf_TPrimitiveLinkedListIterator class; to create an iterator, you must use the somf_TPrimitiveLinkedListIteratorInit method.
Referring to the inheritance hierarchy above, you will note that somf_THashTable inherits from somf_MCollectible, and somf_TPrimitiveLinkedList inherits directly from SOMObject (the topmost class in the inheritance hierarchy). Hence, there is no somf_CreateIterator method, as there is for those classes inheriting from somf_TCollection. For other types of iterators, the somfCreateIterator method of the collection class is used.
For the main collection classes inheriting from the abstract class somf_TCollection, overrides are furnished for the commonly used methods somfAdd, somfCount, somfDeleteAll, somfRemove, and somfCreateIterator.
The various collection classes may have methods extending those defined by the base class. For example, somf_TPriorityQueue adds somfInsert, somfPop, and somfPeek methods. somf_TDeque adds somfAddAfter, somfAddBefore, somfAddLast, and somfAddFirst. It also adds the stack functions somfPop and somfPush.
The somf_THashTable class is an exception to the above, due to its inheritance from somf_MCollectible instead of somf_TCollection. It has specific methods for manipulating the (key, value) pairs of type somf_TAssoc. somf_TPrimitiveLinkedList inherits from SOMObject and includes somfCount, somfRemove, somfAddBefore, somfAddAfter, somfAddFirst, and somfAddLast methods. It does not include a somfCreateIterator method.
Refer to the SOMobjects Developer Toolkit Collection Classes Reference Manual for additional information about methods specific to particular collection classes.
Implementing SOM Collection Classes in an Application
In general, you'll follow these steps to use a collection class in an application:
- Determine the type of collection to be used.
- Define a "collectible" object that is to be placed into the collection by inheriting from the appropriate mixin class.
- Define a collection object for the type of collection being used.
- Define an iterator object for the type of collection being used.
An old programmers' adage says that one good example is worth a thousand pages in a manual. The concepts outlined above seem inadequate to prepare you for the task of implementing a SOM collection in a VisualAge COBOL application. To alleviate the shortcomings in the text, I now illustrate the techniques with an example. This example implements a collection of type set in which duplicates are not allowed, and their order in the collection is of no consequence. The illustration demonstrates the commonly used methods of somf_TCollection, as overridden by somf_TSet. The example also shows the use of an iterator on the collection.
The application is a contrived order-entry system consisting of four modules: a client program, an order, order items, and a user interface. Order items are the collected objects, and they are contained in the collection defined in the order object. The client program instantiates the order, creates the order items, and invokes the appropriate methods in the order object to add the order items.
To keep things simple, the example uses a command line interface instead of a GUI. Using the command line interface also serves to make the example portable. This is implemented with a user interface object, which is also instantiated and invoked by the client program. The user interface prompts the system user for input and displays the application's output to the user.
I used IBM's VisualAge for COBOL for OS/2 V1.1 with the FixPak 1 Corrective Service Diskette applied. The relevant compiler option was PGMNAME(MIXED), which allows mixed case names (necessary because SOM uses mixed case), and TYPECHK, which allows method interfaces to be typechecked against their definitions in the SOM interface repository. If you don't want to place your classes in an interface repository, turn typechecking off by specifying NOTYPECHK on the PROCESS statement in the COBOL source. Also required is the SOM Developer's Toolkit, release 2.1, with all the subsequent maintenance applied.
The OrderItem Class
Instantiations of the OrderItem class constitute the collected objects. This class consists of two attributes: an item number and an item cost. It has get and set methods for each of these attributes and an additional method, somfIsEqual. somfIsEqual is a method in somf_MCollectible that must be overridden.
In the overridden somfIsEqual method, we define the state of equality for our collectible objects. Overriding this method is enforced by SOM, so we will receive a runtime error if the method is not present in our class. In our case, if the item numbers and the item costs of two OrderItem objects are equal, then we consider the two objects equal.
Figure 3 shows the CLASS-ID statement for the OrderItem class definition. Of interest is that it inherits from somf_MCollectible instead of SOMObject, as is typical. Of course, somf_MCollectible inherits from SOMObject, so OrderItem ultimately does as well.
CLASS-ID. "OrderItem" INHERITS somf-MCollectible.
Figure 3. OrderItem CLASS-ID Statement
Figure 4 below shows the REPOSITORY for the OrderItem class. Notice that somf-MCollectible is defined to be somf_MCollectible, because COBOL does not accept underscores (_) in class names.
REPOSITORY. CLASS OrderItem IS "OrderItem" CLASS somf-MCollectible IS "somf_MCollectible".
Figure 4. REPOSITORY for the OrderItem Class
Figure 5 shows the overridden somfIsEqual method defined in the class definition of OrderItem. It takes as input the SOM global environment variable (LS-EV, defined as a pointer) and an OrderItem object (theOrderItem, defined as an object reference). It returns a flag indicating if the OrderItem object passed to it as theOrderItem is equal to the OrderItem object upon which the somfIsEqual method is invoked. In other words, we are invoking this method in an OrderItem object, passing to it another OrderItem object, and asking it to compare the two.
Notice also in this method that we are invoking the get methods for the attributes of the passed object (ItemNumber and ItemCost), then comparing them to the attributes of this OrderItem (Item-Number and Item-Cost).
IDENTIFICATION DIVISION. METHOD-ID. "somfIsEqual" OVERRIDE. DATA DIVISION. LOCAL-STORAGE SECTION. 01 ItemNumber PIC X(10). 01 ItemCost PIC 999V99. LINKAGE SECTION. 01 LS-EV USAGE POINTER. 01 theOrderItem USAGE OBJECT REFERENCE OrderItem. 01 theEqualFlag PIC X. PROCEDURE DIVISION USING BY VALUE LS-EV BY VALUE theOrderItem RETURNING theEqualFlag. INVOKE theOrderItem "getNumber" RETURNING ItemNumber. INVOKE theOrderItem "getCost" RETURNING ItemCost. IF(Item-Number = ItemNumber) AND (Item-Cost = ItemCost) THEN MOVE X"01" TO theEqualFlag ELSE MOVE X"00" TO theEqualFlag. EXIT METHOD. END METHOD "somfIsEqual".
Figure 5. somfIsEqual Override
The Order Class
The Order class is straightforward and has the attributes of an order number, an order date, and a collection of OrderItem objects. It also has an iterator defined so that it can iterate through the collection. Its methods include overrides of somDefaultInit and somFree; get and set methods for the simple attributes of date and number; methods to add and remove OrderItem objects from the collection; and a method that calculates the cost of the order by iterating through the collection.
In Figure 6, we see from the CLASS-ID statement that the order class inherits from the mother of all objects, SOMObject.
CLASS-ID. "Order" INHERITS SOMObject.
Figure 6. The Order Class
In the repository coded in Figure 7 below, note that the CLASS statement for SOMCollection points to somf_TSet. This indicates that the collection contained in the Order class will be of type set. Further, note that the CLASS statement for SOMIterator points to somf_TSetIterator. This iterator is needed to iterate through the collection.
REPOSITORY. CLASS SOMObject IS "SOMObject" CLASS SOMCollection IS "somf_TSet" CLASS SOMIterator IS "somf_TSetIterator" CLASS OrderItem IS "OrderItem".
Figure 7. Repository for the Order Class
The working-storage section of the class definition (see Figure 8) shows the simple attributes of Order-Number and Order-Date in addition to the collection, Order-Collection, which is an object reference to SOMCollection. SOMCollection is a somf_TSet collection. The working-storage section also shows the associated iterator, Order-Iterator, which is an object reference to SOMIterator. SOMIterator is a somf_TSetIterator. Finally, WS-EV is a pointer that is used to point to SOM's global environment variable.
WORKING-STORAGE SECTION. 01 Order-Object. 05 Order-Number PIC X(5). 05 Order-Date PIC X(8). 05 Order-Collection USAGE OBJECT REFERENCE SOMCollection. 05 Order-Iterator USAGE OBJECT REFERENCE SOMIterator. 01 WS-EV USAGE POINTER.
Figure 8. Working-Storage Section of Class Definition
In Figure 9, the method somDefaultInit (inherited from SOMObject) is overridden. This override is necessary so that the global environment variable (WS-EV), the collection, and the iterator can be created during the Order object's initialization. The environment variable is obtained by calling somGetGlobalEnvironment.
The collection is created by invoking the somNew method (inherited from SOMObject) on the class SOMCollection (which is a somf_TSet). The collection's handle, Order-Collection, is returned.
After creating the collection, we invoke the somfCreateIterator method (which is inherited from somf_TSet) on its handle. This action creates the iterator of the collection, Order-Iterator, returned from the somfCreateIterator method.
IDENTIFICATION DIVISION. METHOD-ID. "somDefaultInit" OVERRIDE. DATA DIVISION. PROCEDURE DIVISION. CALL "somGetGlobalEnvironment' RETURNING WS-EV. INVOKE SOMCollection "somNew" RETURNING Order-Collection. INVOKE Order-Collection "somfCreateIterator" USING BY VALUE WS-EV RETURNING Order-Iterator. EXIT METHOD. END METHOD "somDefaultInit".
Figure 9. Overriding the somDefaultInit Method
In Figure 10, we override the somFree method inherited from SOMObject. In this method, we are undoing the actions we performed in the somDefaultInit override. (If we don't override somFree in the Order object, the objects in the collection contained in the object won't be destroyed, and a memory leak will occur.) First, the method somfDeleteAll, which is inherited from somf_TSet, is invoked on the collection. This deletes all the objects placed in the collection and frees the storage they occupied.
Next, somFree, inherited from SOMObject, is invoked to delete the iterator and free its storage. The collection is then freed by somFree. Finally, we invoke somFree on the Order object's super class (in this case, SOMObject) to free the Order object and the storage it uses.
IDENTIFICATION DIVISION. METHOD-ID. "somFree" OVERRIDE. DATA DIVISION. PROCEDURE DIVISION. INVOKE Order-Collection "somfDeleteAll" USING BY VALUE WS-EV. INVOKE Order-Iterator "somFree". INVOKE Order-Collection "somFree". INVOKE SUPER "somFree". EXIT METHOD. END METHOD "somFree".
Figure 10. Overriding the somFree Method
Figure 11 shows the working variables used in the addOrderItem method of the Order class. Note that local storage is used in lieu of working storage. Local storage is refreshed each time the method is invoked; working storage will be in the last used state on each invocation.
IDENTIFICATION DIVISION. METHOD-ID. "addOrderItem ". DATA DIVISION. LOCAL-STORAGE SECTION. 01 LSS-Before-Count PIC S9(8) COMP. 01 LSS-After-Count PIC S9(8) COMP. 01 LSS-CollectedOrderItem USAGE OBJECT REFERENCE OrderItem. 01 LSS-theEqualFlag PIC X. 01 LSS-Item-Found-Flag PIC X. 01 LSS-Item-Count PIC S9(8) COMP. 01 LSS-Loop-Count PIC S9(8) COMP.
Figure 11. Working Variables of the addOrderItem Method
Figure 12 illustrates the parameters passed to the addOrderItem method. An OrderItem object is taken as input, and a structure is returned. Because the return clause only allows the method to return a single address, and we are returning two items (LS-Item-Count and LS-Flag), we group them together under LS-Parms to circumvent this limitation.
LINKAGE SECTION. 01 LS-OrderItem USAGE OBJECT REFERENCE OrderItem. 01 LS-Parms. 05 LS-Item-Count PIC S9(8) COMP. 05 LS-Flag PIC X. PROCEDURE DIVISION USING LS-OrderItem RETURNING LS-Parms.
Figure 12. Parameters Passed to the addOrderItem Method
In Figure 13, the somfCount method, inherited from somf_TSet, is invoked on the collection. This method returns a count of the number of objects in the collection. Because nothing has been added thus far in the method, this constitutes a "before" count.
MOVE X"00" TO LSS-Item-Found-Flag. INVOKE Order-Collection "somfCount" USING BY VALUE WS-EV RETURNING LSS-Before-Count. MOVE LSS-Before-Count TO LSS-Item-Count.
Figure 13. Getting the Count of Objects in the Collection
In Figure 14, the somfFirst method is invoked on the iterator object. This method returns the first item in the collection. Notice that we are testing the count obtained in Figure 13 to verify that the collection contains objects before invoking the somfFirst method. Upon the return from the method, the CHECK-EQUAL paragraph (described in Figure 18) is performed.
IF LSS-Item-Count NOT = 0 THEN INVOKE Order-Iterator "somfFirst" USING BY VALUE WS-EV RETURNING LSS-CollectedOrderItem PERFORM CHECK-EQUAL END-IF.
Figure 14. Getting the Collection's First Element
The segment of code in Figure 15 iterates through the collection by invoking somfNext on the iterator object. The somfNext method returns the next object in the collection. (Note that we cannot invoke somfNext first and expect the first element to be returned.) The somfFirst method performs some initialization of the iterator and must be completed before somfNext can be used. After the somfNext method returns, we perform the CHECK-EQUAL paragraph, described in Figure 18.
SUBTRACT 1 FROM LSS-Item-Count. IF LSS-Item-Count > 0 THEN PERFORM VARYING LSS-Loop-Count FROM 1 BY 1 UNTIL LSS-Loop-Count > LSS-Item-Count OR LSS-Item-Found-Flag = X"01" INVOKE Order-Iterator "somfNext" USING BY VALUE WS-EV RETURNING LSS-CollectedOrderItem PERFORM CHECK-EQUAL END-PERFORM END-IF.
Figure 15. Getting Subsequent Elements of the Collection
In Figure 16, the somfAdd method is invoked on the collection. The somfAdd method is passed two parameters: a pointer to the global environment variable and an object reference to the object that is to be added to the collection. Both parameters are passed BY VALUE and not BY REFERENCE, the customary manner used by COBOL programs. Notice that we are checking a flag to see if the OrderItem object was found during our iteration through the collection.
IF LSS-Item-Found-Flag = X"00" THEN INVOKE Order-Collection "somfAdd" USING BY VALUE WS-EV BY VALUE LS-OrderItem END-IF.
Figure 16. Adding an Element to the Collection
In Figure 17, we again invoke somfCount on the collection. This returns an "after" count of the number of objects in the collection. If the OrderItem object was successfully inserted into the collection, the "after" count will be greater than the "before" count, and a flag in the return parameters is set accordingly. If the counts are the same, the object could not be inserted, and the return flag is set appropriately.
INVOKE Order-Collection "somfCount" USING BY VALUE WS-EV RETURNING LSS-After-Count. MOVE LSS-After-Count TO LS-Item-Count. IF LSS-Before-Count = LSS-After-Count THEN MOVE X"01" TO LS-Flag ELSE MOVE X"00" TO LS-Flag END-IF. EXIT METHOD.
Figure 17. Check the Element Count After the Add Operation
Figure 18 shows the paragraph that is performed each time an object is retrieved from the collection. This paragraph invokes the somfIsEqual method of the OrderItem object. (This method is described in Figure 5.) The method is passed the OrderItem object that we want to insert in the collection. In essence, we are retrieving each item in the collection and, for each one returned, we are asking it to compare itself to the object we are attempting to insert.
CHECK-EQUAL. INVOKE LSS-CollectedOrderItem "somfIsEqual" USING BY VALUE WS-EV BY VALUE LS-OrderItem RETURNING LSS-theEqualFlag. IF LSS-theEqualFlag = X"01" THEN MOVE X"01" TO LSS-Item-Found-Flag. END METHOD "addOrderItem".
Figure 18. Invoking the Overridden somfIsEqual Method
Figures 11 through 18 represent the addOrderItem method that adds an OrderItem object to the collection. To remove an item, a removeOrderItem method is coded, which works in a similar fashion.
We iterate through the collection, looking for a match with the object we want to remove. Upon finding a match, the somfRemove method is invoked on the collection and is passed the same parameters as the somfAdd method described above.
It is interesting to note that when we want to remove an item, we must instantiate an object with the appropriate attributes so that we have a model to match against. After we remove the item from the collection, we must then free the model we created.
Figure 19 shows the calculateCost method of the Order object. This method further illustrates the use of an iterator object to iterate through the collection. As we retrieve each element, we invoke its getCost method, which returns the cost attribute of the OrderItem object retrieved. This cost is then accumulated. After the entire collection has been passed, the accumulated cost is returned to the client that invoked the calculateCost method.
IDENTIFICATION DIVISION. METHOD-ID. "calculateCost". DATA DIVISION. LOCAL-STORAGE SECTION. 01 LSS-CollectedOrderItem USAGE Object REFERENCE OrderItem. 01 LSS-Item-Count PIC S9(8) COMP. 01 LSS-Cost PIC 999V99. LINKAGE SECTION. 01 LS-Cost PIC 9(7)V99. PROCEDURE DIVISION RETURNING LS-Cost. MOVE ZERO TO LS-Cost. INVOKE Order-Collection "somfCount" USINg BY VALUE WS-EV RETURNING LSS-Item-Count. IF LSS-Item-Count > 0 THEN INVOKE Order-Iterator "somfFirst" USING BY VALUE WS-EV RETURNING LSS-CollectedOrderItem PERFORM GET-COST END-IF. SUBTRACT 1 FROM LSS-Item-Count. IF LSS-Item-Count > 0 THEN PERFORM LSS-Item-Count TIMES INVOKE Order-Iterator "somfNext" USING BY VALUE WS-EV RETURNING LSS-CollectedOrderItem PERFORM GET-COST END-PERFORM END-IF. EXIT METHOD. GET-COST. INVOKE LSS-CollectedOrderItem "getCost" RETURNING LSS-Cost. ADD LSS-Cost TO LS-Cost. END METHOD "calculateCost".
Figure 19. Iterating through the Collection Elements
Figures 20 through 23 illustrate a point mentioned earlier in this article during the brief discussion of abstract base classes.
Figure 20 shows the methods defined by somf_TCollection, an abstract class. Note that the methods somfAdd, somfRemove, somfDeleteAll, somfCount, and somfCreateIterator are all used in our sample application.
Figure 20. Methods of Abstract Class somf_TCollection
Next, contrast these methods with the overridden methods for somf_TSet, shown in Figure 21. somf_TSet inherits from the base class somf_TCollection and overrides these methods defined in the base class. This same case is also true for the iterator classes.
Figure 21. Overriding Methods of Class somf_TSet
Figure 22 shows the methods defined in the abstract base class somf_TIterator.
Figure 22. Methods of Abstract Class somf_TIterator
Figure 23 shows the overrides for these methods defined in somf_TSetIterator, which inherits from somf_TIterator.
Figure 23. Overriding Methods of Class somf_TSetIterator
Figures 20 through 23 were taken from the SOM class browser included with the IBM Redbook titled SOMobjects: Management Utilities for Distributed SOM (GG24-4479). I strongly recommended this browser for those of you who want use SOM in your application programs.
The Client Program and the UserInterface Class
The UserInterface class is instantiated by the client program and handles input from and output to the system user. As noted earlier, it is a straight command line interface, lacking a GUI. It has no attributes, and its methods are concerned with communicating with the user.
The client program drives the system process. Both the UserInterface class and client program are typical object-oriented COBOL programs and have no references to SOM collection classes. For the sake of brevity, I will not explain them here.
The entire sample application discussed in this article consists of a client program and three class definitions. Complete COBOL source code for the CLIENT, Order, OrderItem, and UserInterface modules is available right here.
Putting It All Together
After you build the application, execute it from an OS/2 command line by invoking the client.exe file. Figure 24 shows a sample execution.
If you want to place your classes in a SOM interface repository (IR), the SOMobjects Developer Toolkit version 2.1 is required. This product includes the necessary interface definition language (IDL) module (mcollect.idl) and the dynamic link library (DLL) module (somuc.dll) for the collection classes. This DLL is also furnished with OS/2 Warp, but not with OS/2 2.1 or 2.11. Building the application in VisualAge COBOL and creating a SOM IR are typical application programming functions outside the scope of the topic at hand.
Figure 24. Sample Execution of the Collection Class
Collection Classes: Important in Object-Oriented Programming
The techniques illustrated in the second part of this article are typical of the procedures used to implement collections. You can also use alternatives such as placing a C++ interface between the COBOL program and the collections. Insulating the COBOL program (and programmer) in such a manner only delays the learning curve that COBOL programmers must undergo in an object-oriented programming environment.
Using collection classes is an important part of object-oriented programming, and learning and applying the necessary techniques for implementing them should become part of the VisualAge COBOL programmer's repertoire.