Chapter 37 Exercises: Object-Oriented COBOL
These exercises are organized into five tiers of increasing difficulty. Work through them sequentially to build mastery, or jump to the tier that matches your current skill level.
Tier 1: Recall (Exercises 1-7)
These exercises cover the fundamental syntax and terminology of Object-Oriented COBOL.
Exercise 1: Basic CLASS-ID Definition
Write the minimal skeleton for an OO COBOL class named SimpleCounter that includes:
1. A CLASS-ID paragraph.
2. A FACTORY paragraph (empty).
3. An OBJECT paragraph (empty).
Ensure the program compiles (with stubs) and follows the correct OO COBOL structure.
Solution:
IDENTIFICATION DIVISION.
CLASS-ID. SimpleCounter.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
IDENTIFICATION DIVISION.
FACTORY.
PROCEDURE DIVISION.
END FACTORY.
IDENTIFICATION DIVISION.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNT PIC 9(8) VALUE 0.
PROCEDURE DIVISION.
END OBJECT.
END CLASS SimpleCounter.
Exercise 2: Identifying OO Keywords
List the five primary OO-specific keywords introduced in OO COBOL and briefly describe the purpose of each: 1. CLASS-ID 2. METHOD-ID 3. INVOKE 4. OBJECT 5. FACTORY
Solution:
- CLASS-ID -- Declares the name of a class definition and optionally specifies inheritance via the INHERITS clause.
- METHOD-ID -- Declares a method within a class. Methods contain the executable logic of a class.
- INVOKE -- Calls a method on an object instance or a factory method on a class. Equivalent to a method call in other OO languages.
- OBJECT -- Defines the instance portion of a class, including instance data (attributes) and instance methods.
- FACTORY -- Defines the class-level (static) portion of a class, including factory data and factory methods such as constructors.
Exercise 3: Object Reference Declarations
Write the WORKING-STORAGE entries needed to declare:
1. An object reference for a class called Customer.
2. An object reference for a class called Account.
3. A universal object reference that can hold any class type.
Solution:
WORKING-STORAGE SECTION.
01 WS-CUSTOMER-REF USAGE OBJECT REFERENCE Customer.
01 WS-ACCOUNT-REF USAGE OBJECT REFERENCE Account.
01 WS-UNIVERSAL-REF USAGE OBJECT REFERENCE.
Exercise 4: INVOKE Syntax Practice
Given an object reference WS-ACCT-REF of type Account, write the INVOKE statements to:
1. Call the method GetBalance and receive the result into WS-BALANCE.
2. Call the method Deposit passing WS-AMOUNT as a parameter.
3. Call the factory method New on the Account class and store the result in WS-ACCT-REF.
Solution:
INVOKE WS-ACCT-REF "GetBalance"
RETURNING WS-BALANCE
END-INVOKE
INVOKE WS-ACCT-REF "Deposit"
USING WS-AMOUNT
END-INVOKE
INVOKE Account "New"
RETURNING WS-ACCT-REF
END-INVOKE
Exercise 5: Distinguishing Factory and Object
Explain the difference between the FACTORY paragraph and the OBJECT paragraph in an OO COBOL class definition. For each of the following, state whether it belongs in FACTORY or OBJECT: 1. A counter that tracks the total number of instances created. 2. A method that returns an individual employee's name. 3. A method that creates and returns a new instance of the class. 4. An attribute that stores a specific customer's address. 5. A method that returns the maximum account number ever assigned.
Solution:
- FACTORY -- Instance count is class-level data shared across all instances.
- OBJECT -- Individual data retrieval is an instance method.
- FACTORY -- Object creation (constructors) are factory methods.
- OBJECT -- A specific customer's address is instance-level data.
- FACTORY -- The maximum account number assigned is class-level tracking data.
Exercise 6: INHERITS Clause
Write the CLASS-ID paragraph for a class called SavingsAccount that inherits from a base class called Account. Include the REPOSITORY paragraph that references both classes.
Solution:
IDENTIFICATION DIVISION.
CLASS-ID. SavingsAccount
INHERITS Account.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Account
CLASS SavingsAccount.
Exercise 7: Method Signature Reading
Read the following method definition and answer the questions below:
IDENTIFICATION DIVISION.
METHOD-ID. CalculateInterest.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DAILY-RATE PIC 9V9(8).
LINKAGE SECTION.
01 LS-PRINCIPAL PIC 9(9)V99.
01 LS-RATE PIC 9V9(4).
01 LS-DAYS PIC 9(3).
01 LS-INTEREST PIC 9(9)V99.
PROCEDURE DIVISION
USING LS-PRINCIPAL LS-RATE LS-DAYS
RETURNING LS-INTEREST.
BEGIN.
COMPUTE WS-DAILY-RATE = LS-RATE / 365
COMPUTE LS-INTEREST =
LS-PRINCIPAL * WS-DAILY-RATE * LS-DAYS
.
END METHOD CalculateInterest.
- What is the method name?
- How many input parameters does it accept?
- What does the method return?
- What is
WS-DAILY-RATEand why is it in WORKING-STORAGE rather than LINKAGE?
Solution:
- The method name is
CalculateInterest. - It accepts three input parameters:
LS-PRINCIPAL,LS-RATE, andLS-DAYS. - It returns
LS-INTEREST, a numeric value representing calculated interest (PIC 9(9)V99). WS-DAILY-RATEis a local working variable used for intermediate computation. It is in WORKING-STORAGE because it is internal to the method and not passed in or out by the caller.
Tier 2: Understand (Exercises 8-14)
These exercises require you to explain concepts, compare approaches, and trace through OO COBOL code.
Exercise 8: Tracing Object Creation and Method Calls
Trace through the following code and determine the final value of WS-RESULT:
WORKING-STORAGE SECTION.
01 WS-COUNTER-REF USAGE OBJECT REFERENCE Counter.
01 WS-RESULT PIC 9(8) VALUE 0.
PROCEDURE DIVISION.
INVOKE Counter "New" RETURNING WS-COUNTER-REF
INVOKE WS-COUNTER-REF "Increment"
INVOKE WS-COUNTER-REF "Increment"
INVOKE WS-COUNTER-REF "Increment"
INVOKE WS-COUNTER-REF "GetValue"
RETURNING WS-RESULT
DISPLAY "Result: " WS-RESULT
STOP RUN.
Assume the Counter class initializes its internal count to 0 and Increment adds 1 each time.
Solution:
The factory method New creates a Counter instance with an internal count of 0. Three calls to Increment add 1 each time, bringing the internal count to 3. The GetValue method returns 3, so WS-RESULT is 00000003. The display output is Result: 00000003.
Exercise 9: Inheritance vs. Composition
Explain the difference between inheritance and composition in OO COBOL. For each scenario below, state which approach is more appropriate and why:
- A
SavingsAccountis a specialized type ofAccountwith an interest rate. - A
Customerhas anAddressthat can change independently. - A
CheckingAccountis a specialized type ofAccountwith overdraft protection. - A
TransactionLogrecords entries for multiple different account types.
Solution:
Inheritance creates an "is-a" relationship. The child class inherits all data and methods from the parent. Composition creates a "has-a" relationship. The containing class holds an object reference to the contained class.
- Inheritance -- SavingsAccount IS-A Account. It shares all Account behavior and adds interest-specific behavior.
- Composition -- Customer HAS-A Address. The address is a separate entity that can exist and change independently.
- Inheritance -- CheckingAccount IS-A Account. It shares Account behavior and adds overdraft-specific behavior.
- Composition -- TransactionLog HAS-A reference to accounts. It is not a type of account; it records activity across accounts.
Exercise 10: Method Overriding Explanation
Given the following base class method:
IDENTIFICATION DIVISION.
METHOD-ID. CalculateFee.
DATA DIVISION.
LINKAGE SECTION.
01 LS-FEE PIC 9(5)V99.
PROCEDURE DIVISION
RETURNING LS-FEE.
BEGIN.
MOVE 25.00 TO LS-FEE
.
END METHOD CalculateFee.
Write an overriding method in a subclass PremiumAccount that returns a fee of 10.00 instead. Explain what happens when INVOKE is called on an object reference typed as the base class but holding a PremiumAccount instance.
Solution:
IDENTIFICATION DIVISION.
METHOD-ID. CalculateFee.
DATA DIVISION.
LINKAGE SECTION.
01 LS-FEE PIC 9(5)V99.
PROCEDURE DIVISION
RETURNING LS-FEE.
BEGIN.
MOVE 10.00 TO LS-FEE
.
END METHOD CalculateFee.
When INVOKE is called on a base-class reference that holds a PremiumAccount instance, polymorphism ensures the overridden method in PremiumAccount executes, returning 10.00 rather than 25.00. The runtime dispatches to the actual object type, not the declared reference type. This is dynamic dispatch.
Exercise 11: REPOSITORY Paragraph Purpose
Explain why the REPOSITORY paragraph is required in OO COBOL. What happens if you attempt to use a class name that is not declared in the REPOSITORY? Write a REPOSITORY paragraph for a program that uses the classes Account, SavingsAccount, CheckingAccount, and Customer.
Solution:
The REPOSITORY paragraph registers external class names so the compiler knows they are valid class references rather than ordinary data names. Without a REPOSITORY entry, the compiler cannot resolve the class name and will produce a compilation error.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Account
CLASS SavingsAccount
CLASS CheckingAccount
CLASS Customer.
Exercise 12: Comparing INVOKE with CALL
Describe three key differences between the INVOKE statement (OO COBOL) and the CALL statement (procedural COBOL). When would you use each?
Solution:
- Target: INVOKE targets a method on an object or class; CALL targets a standalone program or entry point. INVOKE uses object references while CALL uses program names.
- Dispatch: INVOKE supports polymorphic dispatch -- the actual method called depends on the runtime type of the object. CALL always invokes the same program regardless of context.
- Encapsulation: INVOKE respects class encapsulation -- only methods exposed in the class interface are callable. CALL requires the caller to know the exact data layout expected by the called program through the USING clause.
Use INVOKE when working with OO class hierarchies where polymorphism and encapsulation are beneficial. Use CALL when interfacing with legacy programs, utility routines, or when the overhead of OO infrastructure is not warranted.
Exercise 13: Object Lifecycle
Describe the complete lifecycle of an object in OO COBOL from creation to destruction. Include in your explanation: 1. How a factory method creates an instance. 2. How instance data is initialized. 3. How methods are invoked on the instance. 4. How the object is eventually released.
Solution:
- Creation: A factory method (typically named
New) is invoked on the class name. Inside, the runtime allocates memory for the instance data defined in the OBJECT paragraph's WORKING-STORAGE. - Initialization: The factory method can set initial values on instance data. VALUE clauses on OBJECT data items provide default values. The factory method returns an object reference to the caller.
- Method invocation: The caller uses INVOKE with the object reference to call instance methods. Each method has access to the instance's data through the implicit
SELFreference. - Release: When no object references point to the instance, it becomes eligible for garbage collection (implementation-dependent). Some implementations require explicit destruction. Setting all references to NULL releases the reference.
Exercise 14: Interface Concept
Explain what an interface is in OO COBOL, how it differs from a class, and why interfaces are useful. Write a skeleton INTERFACE-ID definition for an interface called Printable that declares a single method PrintDetails.
Solution:
An interface defines a contract -- a set of method signatures that implementing classes must provide. Unlike a class, an interface has no data and no method implementations. Interfaces enable polymorphism without requiring an inheritance relationship. Multiple classes can implement the same interface, and code can work with any object that satisfies the interface contract.
IDENTIFICATION DIVISION.
INTERFACE-ID. Printable.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. PrintDetails.
DATA DIVISION.
LINKAGE SECTION.
PROCEDURE DIVISION.
END METHOD PrintDetails.
END INTERFACE Printable.
Tier 3: Apply (Exercises 15-22)
These exercises require writing complete or substantial OO COBOL code.
Exercise 15: Complete Counter Class
Write a complete Counter class with the following:
- FACTORY: A New method that creates and returns a new Counter instance.
- OBJECT data: A count field initialized to 0.
- OBJECT methods: Increment (adds 1), Decrement (subtracts 1, minimum 0), Reset (sets to 0), GetValue (returns current count).
Write a test program that creates a Counter, increments it 5 times, decrements it twice, displays the value, resets it, and displays again.
Solution:
IDENTIFICATION DIVISION.
CLASS-ID. Counter.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Counter.
IDENTIFICATION DIVISION.
FACTORY.
DATA DIVISION.
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. New.
DATA DIVISION.
LINKAGE SECTION.
01 LS-NEW-COUNTER USAGE OBJECT REFERENCE Counter.
PROCEDURE DIVISION
RETURNING LS-NEW-COUNTER.
NEW-COUNTER.
INVOKE SELF "NEW" RETURNING LS-NEW-COUNTER
.
END METHOD New.
END FACTORY.
IDENTIFICATION DIVISION.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNT PIC 9(8) VALUE 0.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. Increment.
DATA DIVISION.
PROCEDURE DIVISION.
DO-INCREMENT.
ADD 1 TO WS-COUNT
.
END METHOD Increment.
IDENTIFICATION DIVISION.
METHOD-ID. Decrement.
DATA DIVISION.
PROCEDURE DIVISION.
DO-DECREMENT.
IF WS-COUNT > 0
SUBTRACT 1 FROM WS-COUNT
END-IF
.
END METHOD Decrement.
IDENTIFICATION DIVISION.
METHOD-ID. Reset.
DATA DIVISION.
PROCEDURE DIVISION.
DO-RESET.
MOVE 0 TO WS-COUNT
.
END METHOD Reset.
IDENTIFICATION DIVISION.
METHOD-ID. GetValue.
DATA DIVISION.
LINKAGE SECTION.
01 LS-VALUE PIC 9(8).
PROCEDURE DIVISION
RETURNING LS-VALUE.
DO-GETVALUE.
MOVE WS-COUNT TO LS-VALUE
.
END METHOD GetValue.
END OBJECT.
END CLASS Counter.
Test program:
IDENTIFICATION DIVISION.
PROGRAM-ID. TestCounter.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Counter.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CTR-REF USAGE OBJECT REFERENCE Counter.
01 WS-VALUE PIC 9(8).
01 WS-IDX PIC 9(2).
PROCEDURE DIVISION.
MAIN-LOGIC.
INVOKE Counter "New" RETURNING WS-CTR-REF
PERFORM 5 TIMES
INVOKE WS-CTR-REF "Increment"
END-PERFORM
INVOKE WS-CTR-REF "Decrement"
INVOKE WS-CTR-REF "Decrement"
INVOKE WS-CTR-REF "GetValue"
RETURNING WS-VALUE
DISPLAY "After 5 increments and 2 decrements: "
WS-VALUE
INVOKE WS-CTR-REF "Reset"
INVOKE WS-CTR-REF "GetValue"
RETURNING WS-VALUE
DISPLAY "After reset: " WS-VALUE
STOP RUN.
Expected output:
After 5 increments and 2 decrements: 00000003
After reset: 00000000
Exercise 16: Employee Class with Getters and Setters
Write an Employee class with:
- Instance data: employee ID (PIC 9(6)), name (PIC X(30)), department (PIC X(20)), salary (PIC 9(7)V99).
- Factory method New that accepts all four fields and creates an initialized instance.
- Getter methods for each field.
- A setter method SetSalary that validates the new salary is positive before applying it.
Solution:
IDENTIFICATION DIVISION.
CLASS-ID. Employee.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Employee.
IDENTIFICATION DIVISION.
FACTORY.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. New.
DATA DIVISION.
LINKAGE SECTION.
01 LS-EMP-ID PIC 9(6).
01 LS-EMP-NAME PIC X(30).
01 LS-EMP-DEPT PIC X(20).
01 LS-EMP-SALARY PIC 9(7)V99.
01 LS-NEW-EMP USAGE OBJECT REFERENCE Employee.
PROCEDURE DIVISION
USING LS-EMP-ID LS-EMP-NAME LS-EMP-DEPT
LS-EMP-SALARY
RETURNING LS-NEW-EMP.
CREATE-EMPLOYEE.
INVOKE SELF "NEW" RETURNING LS-NEW-EMP
INVOKE LS-NEW-EMP "Init"
USING LS-EMP-ID LS-EMP-NAME
LS-EMP-DEPT LS-EMP-SALARY
.
END METHOD New.
END FACTORY.
IDENTIFICATION DIVISION.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-EMP-ID PIC 9(6).
01 WS-EMP-NAME PIC X(30).
01 WS-EMP-DEPT PIC X(20).
01 WS-EMP-SALARY PIC 9(7)V99.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. Init.
DATA DIVISION.
LINKAGE SECTION.
01 LS-ID PIC 9(6).
01 LS-NAME PIC X(30).
01 LS-DEPT PIC X(20).
01 LS-SAL PIC 9(7)V99.
PROCEDURE DIVISION
USING LS-ID LS-NAME LS-DEPT LS-SAL.
DO-INIT.
MOVE LS-ID TO WS-EMP-ID
MOVE LS-NAME TO WS-EMP-NAME
MOVE LS-DEPT TO WS-EMP-DEPT
MOVE LS-SAL TO WS-EMP-SALARY
.
END METHOD Init.
IDENTIFICATION DIVISION.
METHOD-ID. GetID.
DATA DIVISION.
LINKAGE SECTION.
01 LS-ID PIC 9(6).
PROCEDURE DIVISION RETURNING LS-ID.
DO-GET.
MOVE WS-EMP-ID TO LS-ID
.
END METHOD GetID.
IDENTIFICATION DIVISION.
METHOD-ID. GetName.
DATA DIVISION.
LINKAGE SECTION.
01 LS-NAME PIC X(30).
PROCEDURE DIVISION RETURNING LS-NAME.
DO-GET.
MOVE WS-EMP-NAME TO LS-NAME
.
END METHOD GetName.
IDENTIFICATION DIVISION.
METHOD-ID. GetDepartment.
DATA DIVISION.
LINKAGE SECTION.
01 LS-DEPT PIC X(20).
PROCEDURE DIVISION RETURNING LS-DEPT.
DO-GET.
MOVE WS-EMP-DEPT TO LS-DEPT
.
END METHOD GetDepartment.
IDENTIFICATION DIVISION.
METHOD-ID. GetSalary.
DATA DIVISION.
LINKAGE SECTION.
01 LS-SAL PIC 9(7)V99.
PROCEDURE DIVISION RETURNING LS-SAL.
DO-GET.
MOVE WS-EMP-SALARY TO LS-SAL
.
END METHOD GetSalary.
IDENTIFICATION DIVISION.
METHOD-ID. SetSalary.
DATA DIVISION.
LINKAGE SECTION.
01 LS-NEW-SAL PIC 9(7)V99.
01 LS-SUCCESS PIC 9 VALUE 0.
PROCEDURE DIVISION
USING LS-NEW-SAL
RETURNING LS-SUCCESS.
DO-SET.
IF LS-NEW-SAL > 0
MOVE LS-NEW-SAL TO WS-EMP-SALARY
MOVE 1 TO LS-SUCCESS
ELSE
MOVE 0 TO LS-SUCCESS
END-IF
.
END METHOD SetSalary.
END OBJECT.
END CLASS Employee.
Exercise 17: Shape Hierarchy
Create a simple class hierarchy:
- Base class Shape with a method GetArea that returns 0.
- Subclass Rectangle with width and height attributes, overriding GetArea to return width * height.
- Subclass Circle with a radius attribute, overriding GetArea to return 3.14159 * radius * radius.
Write a test program that creates one of each, invokes GetArea, and displays the results.
Solution:
IDENTIFICATION DIVISION.
CLASS-ID. Shape.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Shape.
IDENTIFICATION DIVISION.
FACTORY.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. New.
DATA DIVISION.
LINKAGE SECTION.
01 LS-NEW-OBJ USAGE OBJECT REFERENCE Shape.
PROCEDURE DIVISION RETURNING LS-NEW-OBJ.
CREATE-IT.
INVOKE SELF "NEW" RETURNING LS-NEW-OBJ
.
END METHOD New.
END FACTORY.
IDENTIFICATION DIVISION.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. GetArea.
DATA DIVISION.
LINKAGE SECTION.
01 LS-AREA PIC 9(9)V99.
PROCEDURE DIVISION RETURNING LS-AREA.
CALC-AREA.
MOVE 0 TO LS-AREA
.
END METHOD GetArea.
END OBJECT.
END CLASS Shape.
IDENTIFICATION DIVISION.
CLASS-ID. Rectangle INHERITS Shape.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Shape
CLASS Rectangle.
IDENTIFICATION DIVISION.
FACTORY.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. NewWithDims.
DATA DIVISION.
LINKAGE SECTION.
01 LS-WIDTH PIC 9(5)V99.
01 LS-HEIGHT PIC 9(5)V99.
01 LS-NEW-OBJ USAGE OBJECT REFERENCE Rectangle.
PROCEDURE DIVISION
USING LS-WIDTH LS-HEIGHT
RETURNING LS-NEW-OBJ.
CREATE-IT.
INVOKE SELF "NEW" RETURNING LS-NEW-OBJ
INVOKE LS-NEW-OBJ "Init"
USING LS-WIDTH LS-HEIGHT
.
END METHOD NewWithDims.
END FACTORY.
IDENTIFICATION DIVISION.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-WIDTH PIC 9(5)V99 VALUE 0.
01 WS-HEIGHT PIC 9(5)V99 VALUE 0.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. Init.
DATA DIVISION.
LINKAGE SECTION.
01 LS-W PIC 9(5)V99.
01 LS-H PIC 9(5)V99.
PROCEDURE DIVISION USING LS-W LS-H.
DO-INIT.
MOVE LS-W TO WS-WIDTH
MOVE LS-H TO WS-HEIGHT
.
END METHOD Init.
IDENTIFICATION DIVISION.
METHOD-ID. GetArea.
DATA DIVISION.
LINKAGE SECTION.
01 LS-AREA PIC 9(9)V99.
PROCEDURE DIVISION RETURNING LS-AREA.
CALC-AREA.
COMPUTE LS-AREA = WS-WIDTH * WS-HEIGHT
.
END METHOD GetArea.
END OBJECT.
END CLASS Rectangle.
IDENTIFICATION DIVISION.
CLASS-ID. Circle INHERITS Shape.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Shape
CLASS Circle.
IDENTIFICATION DIVISION.
FACTORY.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. NewWithRadius.
DATA DIVISION.
LINKAGE SECTION.
01 LS-RADIUS PIC 9(5)V99.
01 LS-NEW-OBJ USAGE OBJECT REFERENCE Circle.
PROCEDURE DIVISION
USING LS-RADIUS
RETURNING LS-NEW-OBJ.
CREATE-IT.
INVOKE SELF "NEW" RETURNING LS-NEW-OBJ
INVOKE LS-NEW-OBJ "InitRadius"
USING LS-RADIUS
.
END METHOD NewWithRadius.
END FACTORY.
IDENTIFICATION DIVISION.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-RADIUS PIC 9(5)V99 VALUE 0.
01 WS-PI PIC 9V9(5) VALUE 3.14159.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. InitRadius.
DATA DIVISION.
LINKAGE SECTION.
01 LS-R PIC 9(5)V99.
PROCEDURE DIVISION USING LS-R.
DO-INIT.
MOVE LS-R TO WS-RADIUS
.
END METHOD InitRadius.
IDENTIFICATION DIVISION.
METHOD-ID. GetArea.
DATA DIVISION.
LINKAGE SECTION.
01 LS-AREA PIC 9(9)V99.
PROCEDURE DIVISION RETURNING LS-AREA.
CALC-AREA.
COMPUTE LS-AREA =
WS-PI * WS-RADIUS * WS-RADIUS
.
END METHOD GetArea.
END OBJECT.
END CLASS Circle.
Test program:
IDENTIFICATION DIVISION.
PROGRAM-ID. TestShapes.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Shape
CLASS Rectangle
CLASS Circle.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-RECT-REF USAGE OBJECT REFERENCE Rectangle.
01 WS-CIRC-REF USAGE OBJECT REFERENCE Circle.
01 WS-AREA PIC 9(9)V99.
01 WS-WIDTH PIC 9(5)V99 VALUE 10.00.
01 WS-HEIGHT PIC 9(5)V99 VALUE 5.50.
01 WS-RADIUS PIC 9(5)V99 VALUE 7.00.
PROCEDURE DIVISION.
MAIN-LOGIC.
INVOKE Rectangle "NewWithDims"
USING WS-WIDTH WS-HEIGHT
RETURNING WS-RECT-REF
INVOKE WS-RECT-REF "GetArea"
RETURNING WS-AREA
DISPLAY "Rectangle area: " WS-AREA
INVOKE Circle "NewWithRadius"
USING WS-RADIUS
RETURNING WS-CIRC-REF
INVOKE WS-CIRC-REF "GetArea"
RETURNING WS-AREA
DISPLAY "Circle area: " WS-AREA
STOP RUN.
Exercise 18: Polymorphic Array Processing
Create an array of 3 object references typed as Shape. Populate it with one Shape, one Rectangle, and one Circle. Write a loop that invokes GetArea on each element and displays the result. This demonstrates polymorphism through a uniform interface.
Solution:
IDENTIFICATION DIVISION.
PROGRAM-ID. PolyShapes.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS Shape
CLASS Rectangle
CLASS Circle.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SHAPE-TABLE.
05 WS-SHAPE-REF USAGE OBJECT REFERENCE Shape
OCCURS 3 TIMES.
01 WS-RECT-REF USAGE OBJECT REFERENCE Rectangle.
01 WS-CIRC-REF USAGE OBJECT REFERENCE Circle.
01 WS-SHAPE-TEMP USAGE OBJECT REFERENCE Shape.
01 WS-AREA PIC 9(9)V99.
01 WS-IDX PIC 9.
01 WS-WIDTH PIC 9(5)V99 VALUE 8.00.
01 WS-HEIGHT PIC 9(5)V99 VALUE 4.00.
01 WS-RADIUS PIC 9(5)V99 VALUE 5.00.
PROCEDURE DIVISION.
MAIN-LOGIC.
INVOKE Shape "New"
RETURNING WS-SHAPE-TEMP
SET WS-SHAPE-REF(1) TO WS-SHAPE-TEMP
INVOKE Rectangle "NewWithDims"
USING WS-WIDTH WS-HEIGHT
RETURNING WS-RECT-REF
SET WS-SHAPE-REF(2) TO WS-RECT-REF
INVOKE Circle "NewWithRadius"
USING WS-RADIUS
RETURNING WS-CIRC-REF
SET WS-SHAPE-REF(3) TO WS-CIRC-REF
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > 3
INVOKE WS-SHAPE-REF(WS-IDX) "GetArea"
RETURNING WS-AREA
DISPLAY "Shape " WS-IDX " area: " WS-AREA
END-PERFORM
STOP RUN.
Expected output:
Shape 1 area: 00000000000
Shape 2 area: 00000003200
Shape 3 area: 00000007853
Exercise 19: Factory Method with Validation
Extend the Employee class from Exercise 16 so that the factory New method validates:
1. Employee ID must be greater than 0.
2. Name must not be spaces.
3. Salary must be positive.
If validation fails, the method should return NULL instead of a valid object reference. Write a test that attempts to create both a valid and an invalid employee and checks the result.
Hint: Use SET LS-NEW-EMP TO NULL to return a null reference.
Exercise 20: Implementing a Stack Class
Write a Stack class that provides push/pop operations for integers:
- OBJECT data: An array of 100 integers and a top-of-stack pointer.
- Methods: Push (accepts an integer, returns success/failure), Pop (returns the top integer), IsEmpty (returns 1 or 0), Peek (returns top value without removing).
Write a test that pushes 5 values, peeks at the top, then pops all values displaying each.
Hint: The top-of-stack pointer starts at 0. Push increments it before storing. Pop retrieves, then decrements.
Exercise 21: Class with ToString Method
Write a Date class with year (PIC 9(4)), month (PIC 9(2)), and day (PIC 9(2)) instance data. Include:
- A factory New method that accepts year, month, and day.
- A ToString method that returns the date as "YYYY-MM-DD" (PIC X(10)).
- An IsLeapYear method that returns 1 if the year is a leap year, 0 otherwise.
- A DaysInMonth method that returns the correct number of days for the current month.
Hint: For ToString, use STRING to concatenate year, "-", month, "-", day.
Exercise 22: Composition -- Customer with Address
Write two classes:
- Address class with street (PIC X(40)), city (PIC X(20)), state (PIC X(2)), zip (PIC X(10)), and a Format method returning a single formatted string.
- Customer class that holds an object reference to an Address, with methods to get/set the address and a PrintDetails method that invokes Format on the address.
Hint: The Customer class stores USAGE OBJECT REFERENCE Address in its OBJECT WORKING-STORAGE.
Tier 4: Analyze (Exercises 23-28)
These exercises require evaluating design choices, debugging, and architectural reasoning.
Exercise 23: Debugging an OO COBOL Class
The following class has three bugs. Find and describe each one:
IDENTIFICATION DIVISION.
CLASS-ID. BankAccount.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
IDENTIFICATION DIVISION.
FACTORY.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. New.
DATA DIVISION.
LINKAGE SECTION.
01 LS-INIT-BAL PIC 9(9)V99.
01 LS-NEW-ACCT USAGE OBJECT REFERENCE BankAccount.
PROCEDURE DIVISION
USING LS-INIT-BAL
RETURNING LS-NEW-ACCT.
CREATE-ACCT.
INVOKE SELF "NEW" RETURNING LS-NEW-ACCT
MOVE LS-INIT-BAL TO WS-BALANCE
.
END METHOD New.
END FACTORY.
IDENTIFICATION DIVISION.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BALANCE PIC 9(9)V99 VALUE 0.
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
METHOD-ID. Withdraw.
DATA DIVISION.
LINKAGE SECTION.
01 LS-AMOUNT PIC 9(9)V99.
PROCEDURE DIVISION
USING LS-AMOUNT.
DO-WITHDRAW.
SUBTRACT LS-AMOUNT FROM WS-BALANCE
.
END METHOD Withdraw.
END OBJECT.
END CLASS BankAccount.
Hint: Consider: (1) Can the factory method access instance data directly? (2) Is BankAccount in the REPOSITORY? (3) Does Withdraw validate anything?
Exercise 24: Design Review -- When NOT to Use OO COBOL
For each of the following scenarios, argue whether OO COBOL is appropriate or whether procedural COBOL would be a better choice. Justify your reasoning.
- A batch program that reads a sequential file, applies a fixed tax calculation to each record, and writes the result to another file.
- A system that models 15 types of insurance policies, each with different premium calculations, shared base behavior, and frequent additions of new policy types.
- A simple report program that reads one table and produces one report.
- A middleware layer that must expose multiple transaction types to different front-end systems with a uniform interface.
Hint: Consider factors like complexity, extensibility needs, team expertise, and performance overhead.
Exercise 25: Refactoring Procedural Code to OO
Examine the following procedural COBOL fragment that processes three types of transactions:
2000-PROCESS-TRANSACTION.
EVALUATE WS-TRANS-TYPE
WHEN "DEP"
ADD WS-TRANS-AMOUNT TO WS-BALANCE
ADD 1 TO WS-DEP-COUNT
ADD WS-TRANS-AMOUNT TO WS-DEP-TOTAL
WHEN "WDR"
IF WS-TRANS-AMOUNT > WS-BALANCE
MOVE "INSUFFICIENT FUNDS" TO WS-ERROR
ELSE
SUBTRACT WS-TRANS-AMOUNT FROM WS-BALANCE
ADD 1 TO WS-WDR-COUNT
ADD WS-TRANS-AMOUNT TO WS-WDR-TOTAL
END-IF
WHEN "TRF"
IF WS-TRANS-AMOUNT > WS-BALANCE
MOVE "INSUFFICIENT FUNDS" TO WS-ERROR
ELSE
SUBTRACT WS-TRANS-AMOUNT FROM WS-BALANCE
ADD WS-TRANS-AMOUNT TO WS-TARGET-BALANCE
ADD 1 TO WS-TRF-COUNT
ADD WS-TRANS-AMOUNT TO WS-TRF-TOTAL
END-IF
END-EVALUATE.
Describe how you would refactor this into an OO design. Identify the classes, methods, and inheritance relationships. Explain what benefits the OO version provides and what trade-offs are involved.
Hint: Consider a Transaction base class with subclasses for each type and an Account class with a ProcessTransaction method.
Exercise 26: Analyzing Method Dispatch
Given the following class hierarchy:
Account (base)
+-- SavingsAccount
+-- CheckingAccount
+-- PremiumCheckingAccount
Each class overrides the method CalculateFee. If you have the following code:
01 WS-ACCT-REF USAGE OBJECT REFERENCE Account.
INVOKE CheckingAccount "New"
RETURNING WS-ACCT-REF
INVOKE WS-ACCT-REF "CalculateFee"
RETURNING WS-FEE
Which CalculateFee method executes? Now consider:
INVOKE PremiumCheckingAccount "New"
RETURNING WS-ACCT-REF
INVOKE WS-ACCT-REF "CalculateFee"
RETURNING WS-FEE
Which CalculateFee method executes this time? Explain the dispatch mechanism.
Hint: OO COBOL uses dynamic dispatch based on the actual runtime type of the object, not the declared reference type.
Exercise 27: Interface Segregation Analysis
A team designed a single interface IAccountOperations with these methods:
- GetBalance
- Deposit
- Withdraw
- CalculateInterest
- GenerateStatement
- CloseAccount
- TransferFunds
- ApplyPenalty
Not all account types need all these methods. CalculateInterest applies only to savings accounts. ApplyPenalty applies only to early-withdrawal CDs. TransferFunds needs access to two accounts.
Propose a redesign that splits this into smaller, more focused interfaces. Explain the principle behind your redesign.
Hint: This is the Interface Segregation Principle -- clients should not be forced to depend on methods they do not use.
Exercise 28: Performance Impact Analysis
An architect proposes converting a high-volume batch COBOL program (processing 10 million records) from procedural to OO style, creating an object for each record.
Analyze the performance implications: 1. What is the memory impact of creating 10 million objects? 2. How does INVOKE overhead compare to PERFORM for method calls in a tight loop? 3. What alternative OO design could reduce the overhead while preserving some benefits of encapsulation?
Hint: Consider the flyweight pattern, object pooling, or a hybrid approach where OO is used for the processing logic but not for individual records.
Tier 5: Create (Exercises 29-33)
These exercises require designing and implementing substantial OO COBOL systems.
Exercise 29: Library Management System
Design and implement a library management system using OO COBOL with the following classes:
- LibraryItem (base): title, item ID, checked-out flag, due date.
- Book (subclass): author, ISBN, page count.
- DVD (subclass): director, runtime in minutes, rating.
- Library: a collection of up to 100 items with methods for checkout, return, search by title, and overdue report.
Implement full class definitions and a test program that demonstrates all operations.
Hint: Use a table of object references in the Library class. Demonstrate polymorphism by processing all items through the base LibraryItem reference type.
Exercise 30: Banking System with Multiple Account Types
Design a complete banking system with:
- Account base class: account number, owner name, balance, transaction history (last 50 transactions).
- SavingsAccount: interest rate, minimum balance requirement, monthly interest calculation.
- CheckingAccount: monthly fee, transaction limit, overdraft limit.
- CDAccount: maturity date, early withdrawal penalty, fixed interest rate.
- Bank class: manages up to 200 accounts, provides account lookup, total assets report, and interest processing.
Each account type overrides CalculateMonthlyCharges. Implement the full system with a test driver that creates accounts, performs transactions, and generates reports.
Hint: Use the template method pattern -- define the monthly processing sequence in the base class and override specific steps in subclasses.
Exercise 31: Observer Pattern for Transaction Monitoring
Implement the Observer pattern in OO COBOL:
- TransactionSubject class: maintains a list of up to 10 observers, provides Attach, Detach, and Notify methods.
- TransactionObserver interface: declares an OnTransaction method.
- FraudMonitor class: implements TransactionObserver, flags transactions over $10,000.
- AuditLogger class: implements TransactionObserver, logs all transactions.
- BalanceTracker class: implements TransactionObserver, maintains running totals.
Write a test that processes transactions and shows all three observers reacting.
Hint: Store observer references in a table. The Notify method iterates through the table, invoking OnTransaction on each non-null reference.
Exercise 32: Abstract Factory for Report Generation
Design an abstract factory pattern that produces report components:
- ReportFactory interface: methods CreateHeader, CreateDetailLine, CreateFooter.
- TextReportFactory: produces plain-text formatted components.
- CSVReportFactory: produces comma-separated components.
- FixedWidthReportFactory: produces fixed-width formatted components.
Write a report generator that accepts any factory and produces a complete report. Demonstrate by generating the same data as all three formats.
Hint: Each factory method returns a formatted string. The report generator does not know which format it is producing -- it relies on the factory to handle formatting.
Exercise 33: Full OO Application -- Inventory Control
Design and implement an inventory control system using at least five classes with inheritance, composition, and polymorphism:
- Product (base): product ID, name, unit cost, quantity on hand.
- PerishableProduct (subclass): expiration date, temperature requirement, spoilage check.
- ElectronicProduct (subclass): warranty period, voltage, serial number tracking.
- Warehouse: manages up to 500 products, provides add/remove/find/restock operations.
- PurchaseOrder: line items referencing products, quantities, and calculated totals.
- InventoryReport: generates stock level, valuation, and reorder reports by iterating over products polymorphically.
Include a comprehensive test driver that exercises all classes and demonstrates the key OO concepts: encapsulation, inheritance, polymorphism, and composition.
Hint: The InventoryReport class should work with Product references, demonstrating that PerishableProduct and ElectronicProduct objects can be processed uniformly through the base class interface.
General Guidelines
- All programs should follow the OO COBOL class definition structure.
- Use proper COBOL fixed-format column alignment (columns 7-72) where applicable.
- Include IDENTIFICATION DIVISION for each class, factory, object, and method.
- Use meaningful names: class names in PascalCase, data names with WS- or LS- prefixes.
- Test programs should demonstrate the OO concepts being exercised.
- For Tier 4-5 exercises, focus on design quality and correctness of the OO model.