20 min read

Object-Oriented COBOL represents one of the most significant evolutions of the COBOL language since its inception in 1959. With the COBOL 2002 standard, the language formally embraced the object-oriented paradigm, bringing classes, inheritance...

Chapter 37: Object-Oriented COBOL

Part VIII - Modern COBOL and System Evolution


Object-Oriented COBOL represents one of the most significant evolutions of the COBOL language since its inception in 1959. With the COBOL 2002 standard, the language formally embraced the object-oriented paradigm, bringing classes, inheritance, polymorphism, and interfaces to the world's most widely deployed business programming language. This chapter explores how OO concepts map onto COBOL's unique syntax and semantics, how to build class hierarchies for real-world financial applications, and when the object-oriented approach genuinely benefits COBOL development versus when traditional procedural style remains the better choice.

If you have worked through Chapter 17 on subprograms and modular design, you already understand the foundational concepts of encapsulation and separation of concerns that underpin object-oriented programming. OO COBOL extends those ideas by bundling data and the procedures that operate on that data into cohesive units called classes.


37.1 OO COBOL Overview

37.1.1 The Road to Object-Oriented COBOL

The journey toward object-oriented COBOL began in the early 1990s when the ANSI COBOL committee recognized that modern software engineering practices demanded richer abstraction mechanisms than COBOL's procedural model could provide. The result was a phased introduction of OO features:

  • COBOL 1997 (Draft): Initial OO extensions proposed, including CLASS-ID and METHOD-ID paragraphs.
  • COBOL 2002 (ISO/IEC 1989:2002): Full object-oriented support ratified, including classes, interfaces, inheritance, parameterized classes, and object references.
  • COBOL 2014 (ISO/IEC 1989:2014): Refinements to OO features along with JSON and XML capabilities (covered in Chapter 38).

IBM incorporated OO COBOL features into its Enterprise COBOL compilers beginning with Enterprise COBOL V4 and continuing through the current V6.x releases. Micro Focus Visual COBOL also provides comprehensive OO support, including interoperability with .NET and JVM class libraries.

Important Compiler Note: GnuCOBOL (formerly OpenCOBOL) does not support OO COBOL features as of version 3.x. If you are using GnuCOBOL for learning or development, the OO examples in this chapter will not compile. You will need IBM Enterprise COBOL, Micro Focus Visual COBOL, or another compliant commercial compiler to work with object-oriented COBOL code.

37.1.2 The Class/Object Paradigm in COBOL

In traditional procedural COBOL, you organize code into divisions, sections, and paragraphs. Data is declared in the DATA DIVISION and manipulated by statements in the PROCEDURE DIVISION. Programs communicate through the CALL statement, passing data via parameters (as discussed in Chapter 17).

Object-oriented COBOL introduces several new concepts layered on top of this familiar structure:

OO Concept COBOL Implementation
Class CLASS-ID paragraph defining a template for objects
Object An instance of a class, created at runtime
Method METHOD-ID paragraph containing executable logic
Instance Data WORKING-STORAGE within the OBJECT paragraph
Class Data WORKING-STORAGE within the FACTORY paragraph
Inheritance INHERITS clause on CLASS-ID
Interface INTERFACE-ID paragraph
Polymorphism Method overriding and interface implementation
Encapsulation Data hidden within objects, accessed only through methods
Object Reference A data item that points to an object instance

A COBOL class definition is itself a compilation unit, much like a program. It has its own IDENTIFICATION DIVISION, ENVIRONMENT DIVISION, DATA DIVISION, and PROCEDURE DIVISION. The key difference is that a class contains nested definitions for the FACTORY (class-level behavior) and OBJECT (instance-level behavior) components.


37.2 CLASS-ID, METHOD-ID, OBJECT, and FACTORY Paragraphs

37.2.1 The CLASS-ID Paragraph

The CLASS-ID paragraph replaces the PROGRAM-ID paragraph when you are defining a class rather than a traditional program. It specifies the class name and optionally the parent class from which it inherits.

       IDENTIFICATION DIVISION.
       CLASS-ID. BankAccount INHERITS Base.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base IS "Base"
           CLASS BankAccount IS "BankAccount".

The REPOSITORY paragraph in the CONFIGURATION SECTION maps class names used in the source code to external class names (typically file names or system identifiers). This serves a similar purpose to import statements in Java or Python.

37.2.2 The FACTORY Paragraph

The FACTORY paragraph defines class-level data and methods. Factory data is shared among all instances of the class, analogous to static fields in Java or C++. Factory methods can be invoked on the class itself without creating an instance.

       FACTORY.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-NEXT-ACCOUNT-NUMBER     PIC 9(10) VALUE 1000000001.
       01  WS-TOTAL-ACCOUNTS          PIC 9(8)  VALUE 0.

       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. createAccount.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-NEW-ACCOUNT             OBJECT REFERENCE BankAccount.
       LINKAGE SECTION.
       01  LS-ACCOUNT-REF             OBJECT REFERENCE BankAccount.
       PROCEDURE DIVISION RETURNING LS-ACCOUNT-REF.
           INVOKE BankAccount "NEW"
               RETURNING WS-NEW-ACCOUNT
           ADD 1 TO WS-TOTAL-ACCOUNTS
           SET LS-ACCOUNT-REF TO WS-NEW-ACCOUNT
           GOBACK.
       END METHOD createAccount.

       IDENTIFICATION DIVISION.
       METHOD-ID. getTotalAccounts.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-TOTAL                   PIC 9(8).
       PROCEDURE DIVISION RETURNING LS-TOTAL.
           MOVE WS-TOTAL-ACCOUNTS TO LS-TOTAL
           GOBACK.
       END METHOD getTotalAccounts.

       END FACTORY.

37.2.3 The OBJECT Paragraph

The OBJECT paragraph defines instance-level data and methods. Each object created from the class has its own copy of the instance data. Instance methods operate on the specific object through which they are invoked.

       OBJECT.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ACCOUNT-NUMBER          PIC 9(10).
       01  WS-ACCOUNT-HOLDER          PIC X(50).
       01  WS-BALANCE                 PIC S9(13)V99 VALUE 0.
       01  WS-ACCOUNT-TYPE            PIC X(10).
       01  WS-DATE-OPENED             PIC 9(8).
       01  WS-IS-ACTIVE               PIC 9 VALUE 1.
           88 ACCOUNT-ACTIVE          VALUE 1.
           88 ACCOUNT-CLOSED          VALUE 0.

       PROCEDURE DIVISION.
      * Instance methods are defined here
       END OBJECT.

37.2.4 The METHOD-ID Paragraph

Methods are the OO equivalent of COBOL paragraphs or sections, but they are encapsulated within a class and have access to the object's instance data. Each method has its own IDENTIFICATION DIVISION, optional DATA DIVISION, and PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. deposit.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-TIMESTAMP               PIC 9(14).
       LINKAGE SECTION.
       01  LS-AMOUNT                  PIC S9(13)V99.
       01  LS-STATUS                  PIC 9.
           88 DEPOSIT-SUCCESS         VALUE 0.
           88 DEPOSIT-INVALID-AMOUNT  VALUE 1.
           88 DEPOSIT-ACCOUNT-CLOSED  VALUE 2.
       PROCEDURE DIVISION USING LS-AMOUNT
                          RETURNING LS-STATUS.
           IF NOT ACCOUNT-ACTIVE
               SET DEPOSIT-ACCOUNT-CLOSED TO TRUE
               GOBACK
           END-IF

           IF LS-AMOUNT <= 0
               SET DEPOSIT-INVALID-AMOUNT TO TRUE
               GOBACK
           END-IF

           ADD LS-AMOUNT TO WS-BALANCE
           SET DEPOSIT-SUCCESS TO TRUE
           GOBACK.
       END METHOD deposit.

37.3 Defining Classes: Instance Data and Methods

37.3.1 Complete Class Definition Structure

A complete COBOL class definition follows a specific structural pattern. Understanding this structure is essential before writing any OO COBOL code. The following diagram shows the nesting of components within a class:

CLASS-ID. ClassName
  ENVIRONMENT DIVISION (class-level)
    REPOSITORY (class references)
  FACTORY.
    DATA DIVISION
      WORKING-STORAGE SECTION (class-level data)
    PROCEDURE DIVISION
      METHOD-ID. factoryMethod1 ... END METHOD.
      METHOD-ID. factoryMethod2 ... END METHOD.
  END FACTORY.
  OBJECT.
    DATA DIVISION
      WORKING-STORAGE SECTION (instance data)
    PROCEDURE DIVISION
      METHOD-ID. instanceMethod1 ... END METHOD.
      METHOD-ID. instanceMethod2 ... END METHOD.
  END OBJECT.
END CLASS ClassName.

37.3.2 A Complete Compilable BankAccount Class

The following is a complete, compilable OO COBOL program defining a BankAccount class. This requires IBM Enterprise COBOL V4 or later.

      ******************************************************************
      * BANKACCOUNT CLASS DEFINITION
      * Requires: IBM Enterprise COBOL V4+ or Micro Focus Visual COBOL
      * This class represents a basic bank account with deposit,
      * withdrawal, and balance inquiry capabilities.
      ******************************************************************
       IDENTIFICATION DIVISION.
       CLASS-ID. BankAccount INHERITS Base.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base     IS "Base"
           CLASS BankAccount IS "BankAccount".

      ******************************************************************
      * FACTORY PARAGRAPH - Class-level data and methods
      ******************************************************************
       FACTORY.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-NEXT-ACCT-NUM           PIC 9(10) VALUE 1000000001.
       01  WS-TOTAL-ACCOUNTS-CREATED  PIC 9(8)  VALUE 0.

       PROCEDURE DIVISION.

      *-----------------------------------------------------------------
      * Factory Method: getNextAccountNumber
      * Returns the next available account number and increments
      * the internal counter.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. getNextAccountNumber.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-ACCT-NUM                PIC 9(10).
       PROCEDURE DIVISION RETURNING LS-ACCT-NUM.
           MOVE WS-NEXT-ACCT-NUM TO LS-ACCT-NUM
           ADD 1 TO WS-NEXT-ACCT-NUM
           ADD 1 TO WS-TOTAL-ACCOUNTS-CREATED
           GOBACK.
       END METHOD getNextAccountNumber.

      *-----------------------------------------------------------------
      * Factory Method: getTotalAccountsCreated
      * Returns the count of all accounts ever created.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. getTotalAccountsCreated.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-TOTAL                   PIC 9(8).
       PROCEDURE DIVISION RETURNING LS-TOTAL.
           MOVE WS-TOTAL-ACCOUNTS-CREATED TO LS-TOTAL
           GOBACK.
       END METHOD getTotalAccountsCreated.

       END FACTORY.

      ******************************************************************
      * OBJECT PARAGRAPH - Instance-level data and methods
      ******************************************************************
       OBJECT.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ACCOUNT-NUMBER          PIC 9(10).
       01  WS-HOLDER-NAME             PIC X(50).
       01  WS-BALANCE                 PIC S9(13)V99 VALUE 0.
       01  WS-ACCOUNT-TYPE            PIC X(10) VALUE "CHECKING".
       01  WS-DATE-OPENED             PIC 9(8).
       01  WS-STATUS-FLAG             PIC 9 VALUE 1.
           88 ACCOUNT-ACTIVE          VALUE 1.
           88 ACCOUNT-FROZEN          VALUE 2.
           88 ACCOUNT-CLOSED          VALUE 0.
       01  WS-OVERDRAFT-LIMIT         PIC S9(9)V99 VALUE 0.
       01  WS-TRANSACTION-COUNT       PIC 9(8) VALUE 0.

       PROCEDURE DIVISION.

      *-----------------------------------------------------------------
      * Method: initialize
      * Sets up the account with holder name, type, and assigns
      * an account number from the factory.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. initialize.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ASSIGNED-ACCT-NUM       PIC 9(10).
       01  WS-CURRENT-DATE-DATA.
           05  WS-CURRENT-DATE        PIC 9(8).
           05  WS-CURRENT-TIME        PIC 9(8).
           05  WS-DIFF-FROM-GMT       PIC S9(4).
       LINKAGE SECTION.
       01  LS-HOLDER-NAME             PIC X(50).
       01  LS-ACCOUNT-TYPE            PIC X(10).
       01  LS-OVERDRAFT-LIMIT         PIC S9(9)V99.
       PROCEDURE DIVISION USING LS-HOLDER-NAME
                                LS-ACCOUNT-TYPE
                                LS-OVERDRAFT-LIMIT.
           INVOKE BankAccount "getNextAccountNumber"
               RETURNING WS-ASSIGNED-ACCT-NUM
           MOVE WS-ASSIGNED-ACCT-NUM TO WS-ACCOUNT-NUMBER
           MOVE LS-HOLDER-NAME TO WS-HOLDER-NAME
           MOVE LS-ACCOUNT-TYPE TO WS-ACCOUNT-TYPE
           MOVE LS-OVERDRAFT-LIMIT TO WS-OVERDRAFT-LIMIT
           MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-DATE-DATA
           MOVE WS-CURRENT-DATE TO WS-DATE-OPENED
           SET ACCOUNT-ACTIVE TO TRUE
           MOVE 0 TO WS-BALANCE
           MOVE 0 TO WS-TRANSACTION-COUNT
           GOBACK.
       END METHOD initialize.

      *-----------------------------------------------------------------
      * Method: deposit
      * Adds funds to the account. Validates the amount is positive
      * and the account is active.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. deposit.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-AMOUNT                  PIC S9(13)V99.
       01  LS-RESULT-CODE             PIC 9(2).
           88 RESULT-SUCCESS          VALUE 0.
           88 RESULT-INVALID-AMOUNT   VALUE 1.
           88 RESULT-ACCOUNT-INACTIVE VALUE 2.
       PROCEDURE DIVISION USING LS-AMOUNT
                          RETURNING LS-RESULT-CODE.
           EVALUATE TRUE
               WHEN NOT ACCOUNT-ACTIVE
                   SET RESULT-ACCOUNT-INACTIVE TO TRUE
               WHEN LS-AMOUNT <= 0
                   SET RESULT-INVALID-AMOUNT TO TRUE
               WHEN OTHER
                   ADD LS-AMOUNT TO WS-BALANCE
                   ADD 1 TO WS-TRANSACTION-COUNT
                   SET RESULT-SUCCESS TO TRUE
           END-EVALUATE
           GOBACK.
       END METHOD deposit.

      *-----------------------------------------------------------------
      * Method: withdraw
      * Removes funds from the account. Checks for sufficient
      * balance including any overdraft allowance.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. withdraw.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-AVAILABLE-FUNDS         PIC S9(13)V99.
       LINKAGE SECTION.
       01  LS-AMOUNT                  PIC S9(13)V99.
       01  LS-RESULT-CODE             PIC 9(2).
           88 RESULT-SUCCESS          VALUE 0.
           88 RESULT-INVALID-AMOUNT   VALUE 1.
           88 RESULT-ACCOUNT-INACTIVE VALUE 2.
           88 RESULT-INSUFFICIENT     VALUE 3.
       PROCEDURE DIVISION USING LS-AMOUNT
                          RETURNING LS-RESULT-CODE.
           EVALUATE TRUE
               WHEN NOT ACCOUNT-ACTIVE
                   SET RESULT-ACCOUNT-INACTIVE TO TRUE
               WHEN LS-AMOUNT <= 0
                   SET RESULT-INVALID-AMOUNT TO TRUE
               WHEN OTHER
                   COMPUTE WS-AVAILABLE-FUNDS =
                       WS-BALANCE + WS-OVERDRAFT-LIMIT
                   IF LS-AMOUNT > WS-AVAILABLE-FUNDS
                       SET RESULT-INSUFFICIENT TO TRUE
                   ELSE
                       SUBTRACT LS-AMOUNT FROM WS-BALANCE
                       ADD 1 TO WS-TRANSACTION-COUNT
                       SET RESULT-SUCCESS TO TRUE
                   END-IF
           END-EVALUATE
           GOBACK.
       END METHOD withdraw.

      *-----------------------------------------------------------------
      * Method: getBalance
      * Returns the current account balance.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. getBalance.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-BALANCE                 PIC S9(13)V99.
       PROCEDURE DIVISION RETURNING LS-BALANCE.
           MOVE WS-BALANCE TO LS-BALANCE
           GOBACK.
       END METHOD getBalance.

      *-----------------------------------------------------------------
      * Method: getAccountNumber
      * Returns the account number.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. getAccountNumber.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-ACCT-NUM               PIC 9(10).
       PROCEDURE DIVISION RETURNING LS-ACCT-NUM.
           MOVE WS-ACCOUNT-NUMBER TO LS-ACCT-NUM
           GOBACK.
       END METHOD getAccountNumber.

      *-----------------------------------------------------------------
      * Method: getAccountInfo
      * Returns a formatted string with account details.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. getAccountInfo.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-INFO.
           05  LS-INFO-ACCT-NUM      PIC 9(10).
           05  LS-INFO-HOLDER        PIC X(50).
           05  LS-INFO-TYPE          PIC X(10).
           05  LS-INFO-BALANCE       PIC S9(13)V99.
           05  LS-INFO-STATUS        PIC 9.
           05  LS-INFO-DATE-OPENED   PIC 9(8).
           05  LS-INFO-TXN-COUNT     PIC 9(8).
       PROCEDURE DIVISION RETURNING LS-INFO.
           MOVE WS-ACCOUNT-NUMBER    TO LS-INFO-ACCT-NUM
           MOVE WS-HOLDER-NAME       TO LS-INFO-HOLDER
           MOVE WS-ACCOUNT-TYPE      TO LS-INFO-TYPE
           MOVE WS-BALANCE           TO LS-INFO-BALANCE
           MOVE WS-STATUS-FLAG       TO LS-INFO-STATUS
           MOVE WS-DATE-OPENED       TO LS-INFO-DATE-OPENED
           MOVE WS-TRANSACTION-COUNT TO LS-INFO-TXN-COUNT
           GOBACK.
       END METHOD getAccountInfo.

      *-----------------------------------------------------------------
      * Method: closeAccount
      * Closes the account if the balance is zero.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. closeAccount.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-RESULT-CODE             PIC 9(2).
           88 RESULT-SUCCESS          VALUE 0.
           88 RESULT-BALANCE-NOT-ZERO VALUE 4.
           88 RESULT-ALREADY-CLOSED   VALUE 5.
       PROCEDURE DIVISION RETURNING LS-RESULT-CODE.
           EVALUATE TRUE
               WHEN ACCOUNT-CLOSED
                   SET RESULT-ALREADY-CLOSED TO TRUE
               WHEN WS-BALANCE NOT = 0
                   SET RESULT-BALANCE-NOT-ZERO TO TRUE
               WHEN OTHER
                   SET ACCOUNT-CLOSED TO TRUE
                   SET RESULT-SUCCESS TO TRUE
           END-EVALUATE
           GOBACK.
       END METHOD closeAccount.

       END OBJECT.

       END CLASS BankAccount.

37.3.3 Working-Storage Scope Rules

Understanding the scope of WORKING-STORAGE in OO COBOL is critical:

  • FACTORY WORKING-STORAGE: Data items declared here are shared by all instances of the class. There is exactly one copy of each factory data item, regardless of how many objects are created. This is equivalent to static fields in Java.

  • OBJECT WORKING-STORAGE: Data items declared here belong to each individual object instance. When you create a new object with INVOKE ... "NEW", a fresh copy of all OBJECT WORKING-STORAGE items is allocated.

  • METHOD WORKING-STORAGE: Data items declared within a METHOD-ID paragraph are local to that method invocation. They are allocated when the method is entered and deallocated when it returns. This is equivalent to local variables.

  • METHOD LINKAGE SECTION: Parameters passed to the method and the return value are declared here. The PROCEDURE DIVISION USING/RETURNING clause specifies which items are inputs and which are the output.


37.4 Object Creation and References

37.4.1 The INVOKE Statement

The INVOKE statement is the primary mechanism for calling methods on objects and classes. It replaces the CALL statement when working with OO COBOL code, although CALL continues to work for traditional procedural invocations.

      * Creating a new object instance
       INVOKE BankAccount "NEW" RETURNING WS-ACCOUNT-REF

      * Calling an instance method
       INVOKE WS-ACCOUNT-REF "deposit"
           USING WS-DEPOSIT-AMOUNT
           RETURNING WS-RESULT

      * Calling a factory (class-level) method
       INVOKE BankAccount "getTotalAccountsCreated"
           RETURNING WS-TOTAL

The general syntax of the INVOKE statement is:

INVOKE {object-reference | class-name} "method-name"
    [USING parameter-1 parameter-2 ...]
    [RETURNING result-item]

The method name is specified as a literal string. While this may seem unusual compared to languages like Java where method calls use dot notation, it aligns with COBOL's tradition of using literals for external references (similar to how CALL uses string literals for program names, as discussed in Chapter 17).

37.4.2 Object References

Object references are special data items that hold pointers to object instances. They are declared using the OBJECT REFERENCE phrase:

       01  WS-ACCOUNT-REF    OBJECT REFERENCE BankAccount.
       01  WS-GENERIC-REF    OBJECT REFERENCE.
       01  WS-SAVINGS-REF    OBJECT REFERENCE SavingsAccount.

Key rules for object references:

  • A typed object reference (e.g., OBJECT REFERENCE BankAccount) can hold a reference to an instance of BankAccount or any class that inherits from BankAccount.
  • An untyped object reference (OBJECT REFERENCE without a class name) can hold a reference to any object. This is analogous to the Object type in Java, but it provides less compile-time type safety.
  • Object references can be compared using IF ref-1 = ref-2 to check whether two references point to the same object instance.
  • The special value NULL represents a reference that points to no object. You can test for this with IF WS-ACCOUNT-REF = NULL.
  • You can use SET WS-REF-1 TO WS-REF-2 to copy an object reference (both will then point to the same object).

37.4.3 Object Lifecycle

Objects in OO COBOL follow a create-use-destroy lifecycle:

      ******************************************************************
      * CLIENT PROGRAM - Demonstrating object lifecycle
      * Requires: IBM Enterprise COBOL V4+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. AccountDemo.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS BankAccount IS "BankAccount".

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ACCOUNT-1       OBJECT REFERENCE BankAccount.
       01  WS-ACCOUNT-2       OBJECT REFERENCE BankAccount.
       01  WS-BALANCE         PIC S9(13)V99.
       01  WS-RESULT          PIC 9(2).
       01  WS-HOLDER-NAME     PIC X(50).
       01  WS-ACCT-TYPE       PIC X(10).
       01  WS-OVERDRAFT       PIC S9(9)V99.
       01  WS-DEPOSIT-AMT     PIC S9(13)V99.
       01  WS-WITHDRAW-AMT    PIC S9(13)V99.
       01  WS-TOTAL-CREATED   PIC 9(8).
       01  WS-DISPLAY-BAL     PIC -(13)9.99.
       01  WS-ACCT-NUM        PIC 9(10).

       PROCEDURE DIVISION.
       MAIN-LOGIC.
      *    Create first account
           INVOKE BankAccount "NEW"
               RETURNING WS-ACCOUNT-1
           MOVE "JOHN SMITH" TO WS-HOLDER-NAME
           MOVE "CHECKING" TO WS-ACCT-TYPE
           MOVE 500.00 TO WS-OVERDRAFT
           INVOKE WS-ACCOUNT-1 "initialize"
               USING WS-HOLDER-NAME
                     WS-ACCT-TYPE
                     WS-OVERDRAFT

      *    Create second account
           INVOKE BankAccount "NEW"
               RETURNING WS-ACCOUNT-2
           MOVE "JANE DOE" TO WS-HOLDER-NAME
           MOVE "SAVINGS" TO WS-ACCT-TYPE
           MOVE 0 TO WS-OVERDRAFT
           INVOKE WS-ACCOUNT-2 "initialize"
               USING WS-HOLDER-NAME
                     WS-ACCT-TYPE
                     WS-OVERDRAFT

      *    Perform transactions on account 1
           MOVE 5000.00 TO WS-DEPOSIT-AMT
           INVOKE WS-ACCOUNT-1 "deposit"
               USING WS-DEPOSIT-AMT
               RETURNING WS-RESULT
           IF WS-RESULT = 0
               DISPLAY "Deposit successful"
           ELSE
               DISPLAY "Deposit failed with code: " WS-RESULT
           END-IF

      *    Withdraw from account 1
           MOVE 1500.00 TO WS-WITHDRAW-AMT
           INVOKE WS-ACCOUNT-1 "withdraw"
               USING WS-WITHDRAW-AMT
               RETURNING WS-RESULT
           IF WS-RESULT = 0
               DISPLAY "Withdrawal successful"
           ELSE
               DISPLAY "Withdrawal failed with code: " WS-RESULT
           END-IF

      *    Check balance
           INVOKE WS-ACCOUNT-1 "getBalance"
               RETURNING WS-BALANCE
           MOVE WS-BALANCE TO WS-DISPLAY-BAL
           INVOKE WS-ACCOUNT-1 "getAccountNumber"
               RETURNING WS-ACCT-NUM
           DISPLAY "Account " WS-ACCT-NUM
                   " Balance: $" WS-DISPLAY-BAL

      *    Check factory data
           INVOKE BankAccount "getTotalAccountsCreated"
               RETURNING WS-TOTAL-CREATED
           DISPLAY "Total accounts created: " WS-TOTAL-CREATED

           STOP RUN.

37.5 Inheritance

37.5.1 The INHERITS Clause

Inheritance allows you to create new classes that extend existing ones, inheriting their data definitions and methods while adding new capabilities or overriding existing behavior. In COBOL, inheritance is specified using the INHERITS clause on the CLASS-ID paragraph.

       IDENTIFICATION DIVISION.
       CLASS-ID. SavingsAccount INHERITS BankAccount.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base          IS "Base"
           CLASS BankAccount   IS "BankAccount"
           CLASS SavingsAccount IS "SavingsAccount".

The child class (SavingsAccount) inherits all OBJECT WORKING-STORAGE data items and all methods from the parent class (BankAccount). It can then add new data items and methods or override inherited methods.

37.5.2 Method Overriding

When a child class defines a method with the same name as a method in the parent class, the child's method overrides the parent's. The OVERRIDE clause makes this explicit:

      ******************************************************************
      * SAVINGSACCOUNT CLASS - Inherits from BankAccount
      * Adds interest rate and interest calculation capability.
      * Overrides withdraw to enforce minimum balance.
      ******************************************************************
       IDENTIFICATION DIVISION.
       CLASS-ID. SavingsAccount INHERITS BankAccount.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base            IS "Base"
           CLASS BankAccount     IS "BankAccount"
           CLASS SavingsAccount  IS "SavingsAccount".

       FACTORY.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DEFAULT-INTEREST-RATE   PIC 9V9(4) VALUE 0.0250.

       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. getDefaultInterestRate.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-RATE                    PIC 9V9(4).
       PROCEDURE DIVISION RETURNING LS-RATE.
           MOVE WS-DEFAULT-INTEREST-RATE TO LS-RATE
           GOBACK.
       END METHOD getDefaultInterestRate.

       IDENTIFICATION DIVISION.
       METHOD-ID. setDefaultInterestRate.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-RATE                    PIC 9V9(4).
       PROCEDURE DIVISION USING LS-RATE.
           MOVE LS-RATE TO WS-DEFAULT-INTEREST-RATE
           GOBACK.
       END METHOD setDefaultInterestRate.

       END FACTORY.

       OBJECT.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-INTEREST-RATE          PIC 9V9(4).
       01  WS-MINIMUM-BALANCE        PIC S9(13)V99 VALUE 100.00.
       01  WS-ACCRUED-INTEREST       PIC S9(13)V99 VALUE 0.

       PROCEDURE DIVISION.

      *-----------------------------------------------------------------
      * Method: initSavings
      * Extended initialization for savings-specific fields.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. initSavings.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-INTEREST-RATE          PIC 9V9(4).
       01  LS-MIN-BALANCE            PIC S9(13)V99.
       PROCEDURE DIVISION USING LS-INTEREST-RATE
                                LS-MIN-BALANCE.
           MOVE LS-INTEREST-RATE TO WS-INTEREST-RATE
           MOVE LS-MIN-BALANCE TO WS-MINIMUM-BALANCE
           MOVE 0 TO WS-ACCRUED-INTEREST
           GOBACK.
       END METHOD initSavings.

      *-----------------------------------------------------------------
      * Method: withdraw (OVERRIDE)
      * Overrides parent withdraw to enforce minimum balance
      * requirement on savings accounts.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. withdraw OVERRIDE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CURRENT-BALANCE        PIC S9(13)V99.
       01  WS-PROJECTED-BALANCE      PIC S9(13)V99.
       LINKAGE SECTION.
       01  LS-AMOUNT                  PIC S9(13)V99.
       01  LS-RESULT-CODE             PIC 9(2).
           88 RESULT-SUCCESS          VALUE 0.
           88 RESULT-INVALID-AMOUNT   VALUE 1.
           88 RESULT-ACCOUNT-INACTIVE VALUE 2.
           88 RESULT-INSUFFICIENT     VALUE 3.
           88 RESULT-BELOW-MINIMUM    VALUE 6.
       PROCEDURE DIVISION USING LS-AMOUNT
                          RETURNING LS-RESULT-CODE.
      *    First get current balance via inherited method
           INVOKE SELF "getBalance"
               RETURNING WS-CURRENT-BALANCE

           IF LS-AMOUNT <= 0
               SET RESULT-INVALID-AMOUNT TO TRUE
               GOBACK
           END-IF

      *    Check if withdrawal would go below minimum
           COMPUTE WS-PROJECTED-BALANCE =
               WS-CURRENT-BALANCE - LS-AMOUNT

           IF WS-PROJECTED-BALANCE < WS-MINIMUM-BALANCE
               SET RESULT-BELOW-MINIMUM TO TRUE
               GOBACK
           END-IF

      *    Delegate to parent's withdraw via SUPER
           INVOKE SUPER "withdraw"
               USING LS-AMOUNT
               RETURNING LS-RESULT-CODE
           GOBACK.
       END METHOD withdraw.

      *-----------------------------------------------------------------
      * Method: calculateInterest
      * Computes interest on the current balance and adds it
      * to the accrued interest total.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. calculateInterest.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CURRENT-BALANCE        PIC S9(13)V99.
       01  WS-INTEREST-AMOUNT        PIC S9(13)V99.
       LINKAGE SECTION.
       01  LS-INTEREST               PIC S9(13)V99.
       PROCEDURE DIVISION RETURNING LS-INTEREST.
           INVOKE SELF "getBalance"
               RETURNING WS-CURRENT-BALANCE
           COMPUTE WS-INTEREST-AMOUNT ROUNDED =
               WS-CURRENT-BALANCE * WS-INTEREST-RATE / 12
           ADD WS-INTEREST-AMOUNT TO WS-ACCRUED-INTEREST
           MOVE WS-INTEREST-AMOUNT TO LS-INTEREST
           GOBACK.
       END METHOD calculateInterest.

      *-----------------------------------------------------------------
      * Method: applyAccruedInterest
      * Deposits all accrued interest into the account balance.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. applyAccruedInterest.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DEPOSIT-RESULT         PIC 9(2).
       LINKAGE SECTION.
       01  LS-APPLIED-AMOUNT         PIC S9(13)V99.
       PROCEDURE DIVISION RETURNING LS-APPLIED-AMOUNT.
           MOVE WS-ACCRUED-INTEREST TO LS-APPLIED-AMOUNT
           IF WS-ACCRUED-INTEREST > 0
               INVOKE SELF "deposit"
                   USING WS-ACCRUED-INTEREST
                   RETURNING WS-DEPOSIT-RESULT
               MOVE 0 TO WS-ACCRUED-INTEREST
           END-IF
           GOBACK.
       END METHOD applyAccruedInterest.

       END OBJECT.

       END CLASS SavingsAccount.

37.5.3 SELF and SUPER References

Two special object references are available within instance methods:

  • SELF: Refers to the current object instance on which the method was invoked. Use SELF when you want to call another method on the same object. This ensures polymorphic dispatch: if the method has been overridden in a subclass, the subclass version will be called.

  • SUPER: Refers to the parent class's implementation of a method. Use SUPER when you have overridden a method but still want to invoke the parent's version as part of the child's logic. In the SavingsAccount withdraw example above, INVOKE SUPER "withdraw" calls BankAccount's original withdraw method after the minimum balance check passes.


37.6 Interfaces

37.6.1 INTERFACE-ID

Interfaces define a contract of methods that implementing classes must provide, without specifying the implementation. This is the COBOL equivalent of Java interfaces or C++ abstract classes with pure virtual methods.

      ******************************************************************
      * AUDITABLE INTERFACE
      * Any class implementing this interface must provide methods
      * for audit trail functionality.
      ******************************************************************
       IDENTIFICATION DIVISION.
       INTERFACE-ID. Auditable.

       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. getAuditTrail.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-AUDIT-DATA             PIC X(500).
       PROCEDURE DIVISION RETURNING LS-AUDIT-DATA.
       END METHOD getAuditTrail.

       IDENTIFICATION DIVISION.
       METHOD-ID. getLastModifiedDate.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-MOD-DATE               PIC 9(8).
       PROCEDURE DIVISION RETURNING LS-MOD-DATE.
       END METHOD getLastModifiedDate.

       IDENTIFICATION DIVISION.
       METHOD-ID. getModifiedBy.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-USER-ID                PIC X(8).
       PROCEDURE DIVISION RETURNING LS-USER-ID.
       END METHOD getModifiedBy.

       END INTERFACE Auditable.

37.6.2 Implementing Interfaces

A class implements an interface by listing it in the CLASS-ID paragraph and providing concrete implementations of all methods declared in the interface:

       IDENTIFICATION DIVISION.
       CLASS-ID. AuditableBankAccount
           INHERITS BankAccount
           IMPLEMENTS Auditable.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base                IS "Base"
           CLASS BankAccount         IS "BankAccount"
           CLASS AuditableBankAccount IS "AuditableBankAccount"
           INTERFACE Auditable       IS "Auditable".

A class can implement multiple interfaces, providing maximum flexibility:

       CLASS-ID. PremiumAccount
           INHERITS SavingsAccount
           IMPLEMENTS Auditable Reportable Notifiable.

37.6.3 Interface-Based Polymorphism

One of the most powerful uses of interfaces is to write code that works with any class implementing a given interface, regardless of the class's position in the inheritance hierarchy:

      * Process any auditable object
       01  WS-AUDITABLE-REF    OBJECT REFERENCE Auditable.
       01  WS-AUDIT-DATA       PIC X(500).

      * This works with any class implementing Auditable
       INVOKE WS-AUDITABLE-REF "getAuditTrail"
           RETURNING WS-AUDIT-DATA

This allows you to write utility programs that process collections of auditable objects without knowing or caring whether they are bank accounts, loans, or investment portfolios, as long as they implement the Auditable interface.


37.7 Parameterized Classes and Methods

37.7.1 Generic Programming in COBOL

The COBOL 2002 standard introduced parameterized classes and methods, bringing a form of generic programming to COBOL. This feature allows you to write classes and methods that work with different data types specified at instantiation time.

Important Note: Parameterized classes are part of the COBOL 2002 standard but have limited compiler support. IBM Enterprise COBOL does not fully implement parameterized classes as defined in the standard. Micro Focus Visual COBOL provides partial support. This section describes the standard for completeness, but practical usage remains limited.

The concept behind parameterized classes can be illustrated in pseudocode:

CLASS-ID. Collection USING T.
   OBJECT.
      01  WS-ITEMS  T OCCURS 100 TIMES.
      METHOD-ID. add USING item T.
      METHOD-ID. get RETURNING item T.
   END OBJECT.
END CLASS.

37.7.2 Practical Alternatives

Because parameterized classes have limited compiler support, COBOL developers typically achieve similar results through other mechanisms:

  1. Untyped Object References: Use OBJECT REFERENCE (without a type qualifier) to hold references to any object type, then cast as needed.

  2. Interface-Based Generics: Define an interface that all items in a collection must implement, then write the collection class to work with the interface type.

  3. REDEFINES-Based Type Flexibility: Use traditional COBOL REDEFINES to allow a data area to hold different types:

       01  WS-GENERIC-VALUE.
           05  WS-VALUE-TYPE         PIC X.
               88 TYPE-NUMERIC       VALUE "N".
               88 TYPE-ALPHANUMERIC  VALUE "A".
               88 TYPE-OBJECT-REF    VALUE "O".
           05  WS-VALUE-DATA         PIC X(100).
           05  WS-VALUE-NUMERIC REDEFINES WS-VALUE-DATA
                                     PIC S9(18)V99.
           05  WS-VALUE-OBJECT REDEFINES WS-VALUE-DATA
                                     OBJECT REFERENCE.

37.8 OO Design Patterns in COBOL

Design patterns are reusable solutions to commonly occurring problems in software design. While they originated in the object-oriented programming community around languages like Smalltalk, C++, and Java, several patterns translate effectively into OO COBOL. Here we examine three patterns especially relevant to financial COBOL applications.

37.8.1 Factory Pattern

The Factory pattern provides a method for creating objects without specifying the exact class of object that will be created. This is particularly useful in banking where different account types share a common interface but have different initialization requirements.

      ******************************************************************
      * ACCOUNTFACTORY CLASS
      * Creates the appropriate account type based on input parameters.
      * Encapsulates the decision logic for account creation.
      ******************************************************************
       IDENTIFICATION DIVISION.
       CLASS-ID. AccountFactory INHERITS Base.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base            IS "Base"
           CLASS BankAccount     IS "BankAccount"
           CLASS SavingsAccount  IS "SavingsAccount"
           CLASS CheckingAccount IS "CheckingAccount"
           CLASS MoneyMarket     IS "MoneyMarket"
           CLASS AccountFactory  IS "AccountFactory".

       FACTORY.
       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. createAccount.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-NEW-SAVINGS       OBJECT REFERENCE SavingsAccount.
       01  WS-NEW-CHECKING      OBJECT REFERENCE CheckingAccount.
       01  WS-NEW-MONEY-MKT     OBJECT REFERENCE MoneyMarket.
       01  WS-DEFAULT-RATE       PIC 9V9(4).
       01  WS-DEFAULT-MIN-BAL    PIC S9(13)V99.
       01  WS-ZERO-OVERDRAFT     PIC S9(9)V99 VALUE 0.
       01  WS-STD-OVERDRAFT      PIC S9(9)V99 VALUE 500.00.
       LINKAGE SECTION.
       01  LS-ACCOUNT-TYPE       PIC X(10).
       01  LS-HOLDER-NAME        PIC X(50).
       01  LS-ACCOUNT-REF        OBJECT REFERENCE BankAccount.
       PROCEDURE DIVISION USING LS-ACCOUNT-TYPE
                                LS-HOLDER-NAME
                          RETURNING LS-ACCOUNT-REF.
           EVALUATE LS-ACCOUNT-TYPE
               WHEN "SAVINGS"
                   INVOKE SavingsAccount "NEW"
                       RETURNING WS-NEW-SAVINGS
                   INVOKE WS-NEW-SAVINGS "initialize"
                       USING LS-HOLDER-NAME
                             LS-ACCOUNT-TYPE
                             WS-ZERO-OVERDRAFT
                   MOVE 0.0250 TO WS-DEFAULT-RATE
                   MOVE 100.00 TO WS-DEFAULT-MIN-BAL
                   INVOKE WS-NEW-SAVINGS "initSavings"
                       USING WS-DEFAULT-RATE
                             WS-DEFAULT-MIN-BAL
                   SET LS-ACCOUNT-REF TO WS-NEW-SAVINGS

               WHEN "CHECKING"
                   INVOKE CheckingAccount "NEW"
                       RETURNING WS-NEW-CHECKING
                   INVOKE WS-NEW-CHECKING "initialize"
                       USING LS-HOLDER-NAME
                             LS-ACCOUNT-TYPE
                             WS-STD-OVERDRAFT
                   SET LS-ACCOUNT-REF TO WS-NEW-CHECKING

               WHEN "MONEYMARKET"
                   INVOKE MoneyMarket "NEW"
                       RETURNING WS-NEW-MONEY-MKT
                   INVOKE WS-NEW-MONEY-MKT "initialize"
                       USING LS-HOLDER-NAME
                             LS-ACCOUNT-TYPE
                             WS-ZERO-OVERDRAFT
                   MOVE 0.0375 TO WS-DEFAULT-RATE
                   MOVE 2500.00 TO WS-DEFAULT-MIN-BAL
                   INVOKE WS-NEW-MONEY-MKT "initSavings"
                       USING WS-DEFAULT-RATE
                             WS-DEFAULT-MIN-BAL
                   SET LS-ACCOUNT-REF TO WS-NEW-MONEY-MKT

               WHEN OTHER
                   SET LS-ACCOUNT-REF TO NULL
           END-EVALUATE
           GOBACK.
       END METHOD createAccount.

       END FACTORY.

       OBJECT.
       PROCEDURE DIVISION.
       END OBJECT.

       END CLASS AccountFactory.

37.8.2 Strategy Pattern

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. In banking, this is ideal for implementing different fee calculation strategies, interest computation methods, or transaction validation rules.

      ******************************************************************
      * INTERESTCALCULATOR INTERFACE
      * Defines the strategy for calculating interest.
      ******************************************************************
       IDENTIFICATION DIVISION.
       INTERFACE-ID. InterestCalculator.

       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. calculateInterest.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-PRINCIPAL              PIC S9(13)V99.
       01  LS-RATE                   PIC 9V9(6).
       01  LS-DAYS                   PIC 9(4).
       01  LS-INTEREST               PIC S9(13)V99.
       PROCEDURE DIVISION USING LS-PRINCIPAL
                                LS-RATE
                                LS-DAYS
                          RETURNING LS-INTEREST.
       END METHOD calculateInterest.

       END INTERFACE InterestCalculator.

      ******************************************************************
      * SIMPLEINTEREST CLASS
      * Implements simple interest calculation: P * R * T
      ******************************************************************
       IDENTIFICATION DIVISION.
       CLASS-ID. SimpleInterest INHERITS Base
           IMPLEMENTS InterestCalculator.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base              IS "Base"
           CLASS SimpleInterest    IS "SimpleInterest"
           INTERFACE InterestCalculator IS "InterestCalculator".

       FACTORY.
       PROCEDURE DIVISION.
       END FACTORY.

       OBJECT.
       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. calculateInterest.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-PRINCIPAL              PIC S9(13)V99.
       01  LS-RATE                   PIC 9V9(6).
       01  LS-DAYS                   PIC 9(4).
       01  LS-INTEREST               PIC S9(13)V99.
       PROCEDURE DIVISION USING LS-PRINCIPAL
                                LS-RATE
                                LS-DAYS
                          RETURNING LS-INTEREST.
           COMPUTE LS-INTEREST ROUNDED =
               LS-PRINCIPAL * LS-RATE * LS-DAYS / 365
           GOBACK.
       END METHOD calculateInterest.

       END OBJECT.
       END CLASS SimpleInterest.

      ******************************************************************
      * COMPOUNDINTEREST CLASS
      * Implements daily compound interest calculation.
      ******************************************************************
       IDENTIFICATION DIVISION.
       CLASS-ID. CompoundInterest INHERITS Base
           IMPLEMENTS InterestCalculator.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base              IS "Base"
           CLASS CompoundInterest  IS "CompoundInterest"
           INTERFACE InterestCalculator IS "InterestCalculator".

       FACTORY.
       PROCEDURE DIVISION.
       END FACTORY.

       OBJECT.
       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. calculateInterest.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DAILY-RATE             PIC 9V9(10).
       01  WS-COMPOUND-FACTOR        PIC 9(13)V9(10).
       01  WS-FINAL-AMOUNT           PIC S9(13)V99.
       LINKAGE SECTION.
       01  LS-PRINCIPAL              PIC S9(13)V99.
       01  LS-RATE                   PIC 9V9(6).
       01  LS-DAYS                   PIC 9(4).
       01  LS-INTEREST               PIC S9(13)V99.
       PROCEDURE DIVISION USING LS-PRINCIPAL
                                LS-RATE
                                LS-DAYS
                          RETURNING LS-INTEREST.
           COMPUTE WS-DAILY-RATE = LS-RATE / 365
           COMPUTE WS-COMPOUND-FACTOR =
               (1 + WS-DAILY-RATE) ** LS-DAYS
           COMPUTE WS-FINAL-AMOUNT ROUNDED =
               LS-PRINCIPAL * WS-COMPOUND-FACTOR
           COMPUTE LS-INTEREST ROUNDED =
               WS-FINAL-AMOUNT - LS-PRINCIPAL
           GOBACK.
       END METHOD calculateInterest.

       END OBJECT.
       END CLASS CompoundInterest.

37.8.3 Observer Pattern

The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified. In banking, this is useful for triggering notifications when account balances cross thresholds, when large transactions occur, or when suspicious activity is detected.

      ******************************************************************
      * ACCOUNTOBSERVER INTERFACE
      * Observers implement this interface to receive notifications
      * about account events.
      ******************************************************************
       IDENTIFICATION DIVISION.
       INTERFACE-ID. AccountObserver.

       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. onTransaction.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-ACCT-NUM               PIC 9(10).
       01  LS-TXN-TYPE               PIC X(10).
       01  LS-AMOUNT                  PIC S9(13)V99.
       01  LS-NEW-BALANCE             PIC S9(13)V99.
       PROCEDURE DIVISION USING LS-ACCT-NUM
                                LS-TXN-TYPE
                                LS-AMOUNT
                                LS-NEW-BALANCE.
       END METHOD onTransaction.

       END INTERFACE AccountObserver.

      ******************************************************************
      * FRAUDDETECTOR CLASS
      * Implements AccountObserver to watch for suspicious activity.
      ******************************************************************
       IDENTIFICATION DIVISION.
       CLASS-ID. FraudDetector INHERITS Base
           IMPLEMENTS AccountObserver.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base            IS "Base"
           CLASS FraudDetector   IS "FraudDetector"
           INTERFACE AccountObserver IS "AccountObserver".

       FACTORY.
       PROCEDURE DIVISION.
       END FACTORY.

       OBJECT.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-LARGE-TXN-THRESHOLD    PIC S9(13)V99
                                      VALUE 10000.00.
       01  WS-ALERT-COUNT            PIC 9(6) VALUE 0.

       PROCEDURE DIVISION.

       IDENTIFICATION DIVISION.
       METHOD-ID. onTransaction.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DISPLAY-AMT            PIC -(13)9.99.
       LINKAGE SECTION.
       01  LS-ACCT-NUM               PIC 9(10).
       01  LS-TXN-TYPE               PIC X(10).
       01  LS-AMOUNT                  PIC S9(13)V99.
       01  LS-NEW-BALANCE             PIC S9(13)V99.
       PROCEDURE DIVISION USING LS-ACCT-NUM
                                LS-TXN-TYPE
                                LS-AMOUNT
                                LS-NEW-BALANCE.
           IF LS-AMOUNT > WS-LARGE-TXN-THRESHOLD
               OR LS-AMOUNT < -5000.00
               ADD 1 TO WS-ALERT-COUNT
               MOVE LS-AMOUNT TO WS-DISPLAY-AMT
               DISPLAY "FRAUD ALERT: Account " LS-ACCT-NUM
               DISPLAY "  Transaction: " LS-TXN-TYPE
               DISPLAY "  Amount: $" WS-DISPLAY-AMT
               DISPLAY "  Alert #" WS-ALERT-COUNT
           END-IF
           GOBACK.
       END METHOD onTransaction.

       END OBJECT.
       END CLASS FraudDetector.

To connect observers to an account, you would extend the BankAccount class to maintain a list of observers and notify them whenever a transaction occurs. The notification loop in the deposit and withdraw methods would iterate through the registered observers and invoke their onTransaction method.


37.9 Mixing OO and Procedural COBOL

37.9.1 The Reality of Migration

In practice, very few organizations rewrite their entire COBOL codebase in the object-oriented style. The existing procedural code works correctly, has been tested for decades, and rewriting it would introduce risk with little immediate business value. Instead, organizations adopt a gradual migration strategy where new functionality is written using OO COBOL while existing procedural code continues to operate alongside it.

37.9.2 Calling Procedural Programs from OO Methods

OO COBOL methods can use the traditional CALL statement to invoke existing procedural programs. This is the most common integration pattern:

       IDENTIFICATION DIVISION.
       METHOD-ID. validateTransaction.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-TXN-RECORD.
           05  WS-TXN-ACCT-NUM      PIC 9(10).
           05  WS-TXN-TYPE          PIC X(2).
           05  WS-TXN-AMOUNT        PIC S9(13)V99.
           05  WS-TXN-DATE          PIC 9(8).
       01  WS-VALIDATION-RESULT     PIC X.
           88 VALIDATION-PASSED     VALUE "P".
           88 VALIDATION-FAILED     VALUE "F".
       LINKAGE SECTION.
       01  LS-AMOUNT                PIC S9(13)V99.
       01  LS-IS-VALID              PIC 9.
       PROCEDURE DIVISION USING LS-AMOUNT
                          RETURNING LS-IS-VALID.
      *    Call existing procedural validation program
      *    (See Chapter 17 for details on CALL and parameter passing)
           INVOKE SELF "getAccountNumber"
               RETURNING WS-TXN-ACCT-NUM
           MOVE "DP" TO WS-TXN-TYPE
           MOVE LS-AMOUNT TO WS-TXN-AMOUNT
           MOVE FUNCTION CURRENT-DATE(1:8) TO WS-TXN-DATE

           CALL "TXNVALID" USING WS-TXN-RECORD
                                  WS-VALIDATION-RESULT

           IF VALIDATION-PASSED
               MOVE 1 TO LS-IS-VALID
           ELSE
               MOVE 0 TO LS-IS-VALID
           END-IF
           GOBACK.
       END METHOD validateTransaction.

37.9.3 Using OO Objects from Procedural Programs

Procedural COBOL programs can create and use OO COBOL objects. The procedural program must include a REPOSITORY paragraph to reference the classes it uses:

      ******************************************************************
      * PROCEDURAL PROGRAM USING OO CLASSES
      * Demonstrates how a traditional COBOL program can leverage
      * OO classes for specific operations while remaining procedural
      * in overall structure.
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. DailyInterestRun.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS SavingsAccount IS "SavingsAccount"
           CLASS AccountFactory IS "AccountFactory".

       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT ACCOUNT-FILE ASSIGN TO "ACCTMAST"
               ORGANIZATION IS INDEXED
               ACCESS MODE IS SEQUENTIAL
               RECORD KEY IS ACCT-KEY
               FILE STATUS IS WS-FILE-STATUS.

       DATA DIVISION.
       FILE SECTION.
       FD  ACCOUNT-FILE.
       01  ACCOUNT-RECORD.
           05  ACCT-KEY              PIC 9(10).
           05  ACCT-HOLDER           PIC X(50).
           05  ACCT-TYPE             PIC X(10).
           05  ACCT-BALANCE          PIC S9(13)V99.
           05  ACCT-INT-RATE         PIC 9V9(4).
           05  ACCT-MIN-BAL          PIC S9(13)V99.
           05  ACCT-STATUS           PIC 9.

       WORKING-STORAGE SECTION.
       01  WS-FILE-STATUS            PIC XX.
       01  WS-EOF-FLAG               PIC 9 VALUE 0.
           88 END-OF-FILE            VALUE 1.
       01  WS-SAVINGS-REF    OBJECT REFERENCE SavingsAccount.
       01  WS-INTEREST-AMT          PIC S9(13)V99.
       01  WS-APPLIED-AMT           PIC S9(13)V99.
       01  WS-HOLDER-NAME           PIC X(50).
       01  WS-ACCT-TYPE-WS          PIC X(10).
       01  WS-OVERDRAFT             PIC S9(9)V99.
       01  WS-INT-RATE              PIC 9V9(4).
       01  WS-MIN-BAL               PIC S9(13)V99.
       01  WS-DEPOSIT-AMT           PIC S9(13)V99.
       01  WS-DEP-RESULT            PIC 9(2).
       01  WS-ACCOUNTS-PROCESSED    PIC 9(8) VALUE 0.
       01  WS-TOTAL-INTEREST        PIC S9(15)V99 VALUE 0.
       01  WS-DISPLAY-AMT           PIC -(15)9.99.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           OPEN INPUT ACCOUNT-FILE
           PERFORM READ-NEXT-ACCOUNT
           PERFORM PROCESS-ACCOUNTS
               UNTIL END-OF-FILE
           CLOSE ACCOUNT-FILE

           DISPLAY "Daily Interest Run Complete"
           DISPLAY "Accounts Processed: " WS-ACCOUNTS-PROCESSED
           MOVE WS-TOTAL-INTEREST TO WS-DISPLAY-AMT
           DISPLAY "Total Interest Calculated: $" WS-DISPLAY-AMT
           STOP RUN.

       READ-NEXT-ACCOUNT.
           READ ACCOUNT-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ.

       PROCESS-ACCOUNTS.
           IF ACCT-TYPE = "SAVINGS" OR "MONEYMARKET"
               AND ACCT-STATUS = 1
      *        Create a temporary OO object to calculate interest
               INVOKE SavingsAccount "NEW"
                   RETURNING WS-SAVINGS-REF
               MOVE ACCT-HOLDER TO WS-HOLDER-NAME
               MOVE ACCT-TYPE TO WS-ACCT-TYPE-WS
               MOVE 0 TO WS-OVERDRAFT
               INVOKE WS-SAVINGS-REF "initialize"
                   USING WS-HOLDER-NAME
                         WS-ACCT-TYPE-WS
                         WS-OVERDRAFT

               MOVE ACCT-INT-RATE TO WS-INT-RATE
               MOVE ACCT-MIN-BAL TO WS-MIN-BAL
               INVOKE WS-SAVINGS-REF "initSavings"
                   USING WS-INT-RATE
                         WS-MIN-BAL

      *        Deposit current balance into the object
               MOVE ACCT-BALANCE TO WS-DEPOSIT-AMT
               INVOKE WS-SAVINGS-REF "deposit"
                   USING WS-DEPOSIT-AMT
                   RETURNING WS-DEP-RESULT

      *        Calculate and apply interest using OO methods
               INVOKE WS-SAVINGS-REF "calculateInterest"
                   RETURNING WS-INTEREST-AMT
               ADD WS-INTEREST-AMT TO WS-TOTAL-INTEREST

               ADD 1 TO WS-ACCOUNTS-PROCESSED
           END-IF
           PERFORM READ-NEXT-ACCOUNT.

37.9.4 Gradual Migration Strategies

Here is a practical roadmap for introducing OO COBOL into an existing procedural codebase:

Phase 1 - Wrapper Classes: Create OO classes that wrap existing procedural programs. The class methods internally use CALL to invoke the existing programs but present an object-oriented interface to new code.

Phase 2 - New Features as Classes: Write all new business functionality as OO COBOL classes. Existing programs continue to run unchanged, but new modules benefit from encapsulation, inheritance, and polymorphism.

Phase 3 - Shared Services: Identify common cross-cutting concerns (logging, validation, error handling) and implement them as reusable OO classes or interfaces. Gradually refactor existing programs to use these shared services.

Phase 4 - Domain Model: Over time, build a domain model of classes that represents the core business entities (accounts, customers, transactions). This model becomes the authoritative representation of business logic, with the legacy programs delegating to it.

This phased approach minimizes risk while steadily modernizing the codebase. The key principle is that procedural and OO COBOL can coexist peacefully within the same application.


37.10 Practical Example: Building a Banking Account Class Hierarchy

37.10.1 Design Overview

Let us build a comprehensive class hierarchy for a banking application. The hierarchy models different types of accounts with shared base functionality and specialized behavior:

BankAccount (base class)
  |-- CheckingAccount
  |     |-- PremiumCheckingAccount
  |-- SavingsAccount
  |     |-- MoneyMarketAccount
  |     |-- CertificateOfDeposit
  |-- LoanAccount

Interfaces: - InterestBearing - for accounts that earn or charge interest - FeeChargeable - for accounts that incur fees - Auditable - for accounts requiring audit trails

37.10.2 CheckingAccount Class

      ******************************************************************
      * CHECKINGACCOUNT CLASS
      * Extends BankAccount with check-writing capability,
      * monthly fees, and free transaction limits.
      ******************************************************************
       IDENTIFICATION DIVISION.
       CLASS-ID. CheckingAccount INHERITS BankAccount.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base             IS "Base"
           CLASS BankAccount      IS "BankAccount"
           CLASS CheckingAccount  IS "CheckingAccount".

       FACTORY.
       PROCEDURE DIVISION.
       END FACTORY.

       OBJECT.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-MONTHLY-FEE            PIC S9(5)V99 VALUE 12.00.
       01  WS-FREE-TXN-LIMIT         PIC 9(4) VALUE 50.
       01  WS-PER-TXN-FEE            PIC S9(3)V99 VALUE 0.50.
       01  WS-MONTHLY-TXN-COUNT      PIC 9(6) VALUE 0.
       01  WS-NEXT-CHECK-NUMBER       PIC 9(6) VALUE 1001.
       01  WS-CHECK-REGISTER.
           05  WS-CHECK-ENTRY OCCURS 100 TIMES.
               10  WS-CHK-NUMBER     PIC 9(6).
               10  WS-CHK-PAYEE      PIC X(50).
               10  WS-CHK-AMOUNT     PIC S9(13)V99.
               10  WS-CHK-DATE       PIC 9(8).
               10  WS-CHK-STATUS     PIC X.
                   88 CHECK-CLEARED  VALUE "C".
                   88 CHECK-PENDING  VALUE "P".
                   88 CHECK-VOIDED   VALUE "V".
       01  WS-CHECK-REG-COUNT        PIC 9(3) VALUE 0.

       PROCEDURE DIVISION.

      *-----------------------------------------------------------------
      * Method: initChecking
      * Initializes checking-specific attributes.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. initChecking.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-MONTHLY-FEE            PIC S9(5)V99.
       01  LS-FREE-TXN-LIMIT         PIC 9(4).
       PROCEDURE DIVISION USING LS-MONTHLY-FEE
                                LS-FREE-TXN-LIMIT.
           MOVE LS-MONTHLY-FEE TO WS-MONTHLY-FEE
           MOVE LS-FREE-TXN-LIMIT TO WS-FREE-TXN-LIMIT
           MOVE 0 TO WS-MONTHLY-TXN-COUNT
           MOVE 0 TO WS-CHECK-REG-COUNT
           GOBACK.
       END METHOD initChecking.

      *-----------------------------------------------------------------
      * Method: writeCheck
      * Writes a check against the account balance.
      * Records the check in the internal register.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. writeCheck.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-WITHDRAW-RESULT        PIC 9(2).
       01  WS-CHECK-IDX              PIC 9(3).
       01  WS-CURRENT-DATE-DATA.
           05  WS-TODAY               PIC 9(8).
           05  FILLER                 PIC X(13).
       LINKAGE SECTION.
       01  LS-PAYEE                   PIC X(50).
       01  LS-AMOUNT                  PIC S9(13)V99.
       01  LS-CHECK-NUM               PIC 9(6).
       01  LS-RESULT                  PIC 9(2).
           88 CHECK-SUCCESS           VALUE 0.
           88 CHECK-NSF               VALUE 3.
           88 CHECK-REGISTER-FULL     VALUE 7.
       PROCEDURE DIVISION USING LS-PAYEE
                                LS-AMOUNT
                          RETURNING LS-CHECK-NUM
                                    LS-RESULT.
           IF WS-CHECK-REG-COUNT >= 100
               SET CHECK-REGISTER-FULL TO TRUE
               MOVE 0 TO LS-CHECK-NUM
               GOBACK
           END-IF

      *    Attempt withdrawal for the check amount
           INVOKE SELF "withdraw"
               USING LS-AMOUNT
               RETURNING WS-WITHDRAW-RESULT

           IF WS-WITHDRAW-RESULT NOT = 0
               SET CHECK-NSF TO TRUE
               MOVE 0 TO LS-CHECK-NUM
               GOBACK
           END-IF

      *    Record check in register
           ADD 1 TO WS-CHECK-REG-COUNT
           MOVE WS-CHECK-REG-COUNT TO WS-CHECK-IDX
           MOVE WS-NEXT-CHECK-NUMBER
               TO WS-CHK-NUMBER(WS-CHECK-IDX)
           MOVE LS-PAYEE TO WS-CHK-PAYEE(WS-CHECK-IDX)
           MOVE LS-AMOUNT TO WS-CHK-AMOUNT(WS-CHECK-IDX)
           MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-DATE-DATA
           MOVE WS-TODAY TO WS-CHK-DATE(WS-CHECK-IDX)
           SET CHECK-PENDING IN
               WS-CHK-STATUS(WS-CHECK-IDX) TO TRUE

           MOVE WS-NEXT-CHECK-NUMBER TO LS-CHECK-NUM
           ADD 1 TO WS-NEXT-CHECK-NUMBER
           ADD 1 TO WS-MONTHLY-TXN-COUNT
           SET CHECK-SUCCESS TO TRUE
           GOBACK.
       END METHOD writeCheck.

      *-----------------------------------------------------------------
      * Method: assessMonthlyFees
      * Calculates and deducts monthly maintenance fee plus any
      * excess transaction fees.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. assessMonthlyFees.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-TOTAL-FEES             PIC S9(9)V99.
       01  WS-EXCESS-TXNS            PIC 9(6).
       01  WS-EXCESS-FEES            PIC S9(9)V99.
       01  WS-FEE-RESULT             PIC 9(2).
       LINKAGE SECTION.
       01  LS-FEES-CHARGED           PIC S9(9)V99.
       PROCEDURE DIVISION RETURNING LS-FEES-CHARGED.
           MOVE WS-MONTHLY-FEE TO WS-TOTAL-FEES

           IF WS-MONTHLY-TXN-COUNT > WS-FREE-TXN-LIMIT
               COMPUTE WS-EXCESS-TXNS =
                   WS-MONTHLY-TXN-COUNT - WS-FREE-TXN-LIMIT
               COMPUTE WS-EXCESS-FEES =
                   WS-EXCESS-TXNS * WS-PER-TXN-FEE
               ADD WS-EXCESS-FEES TO WS-TOTAL-FEES
           END-IF

      *    Deduct fees via withdrawal
           INVOKE SELF "withdraw"
               USING WS-TOTAL-FEES
               RETURNING WS-FEE-RESULT

           MOVE WS-TOTAL-FEES TO LS-FEES-CHARGED
           MOVE 0 TO WS-MONTHLY-TXN-COUNT
           GOBACK.
       END METHOD assessMonthlyFees.

       END OBJECT.

       END CLASS CheckingAccount.

37.10.3 LoanAccount Class

      ******************************************************************
      * LOANACCOUNT CLASS
      * Represents a loan with principal, interest, and payment
      * schedule. Balance is negative (owed by customer).
      ******************************************************************
       IDENTIFICATION DIVISION.
       CLASS-ID. LoanAccount INHERITS BankAccount.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS Base          IS "Base"
           CLASS BankAccount   IS "BankAccount"
           CLASS LoanAccount   IS "LoanAccount".

       FACTORY.
       PROCEDURE DIVISION.
       END FACTORY.

       OBJECT.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ORIGINAL-PRINCIPAL     PIC S9(13)V99.
       01  WS-INTEREST-RATE          PIC 9V9(6).
       01  WS-TERM-MONTHS            PIC 9(4).
       01  WS-MONTHLY-PAYMENT        PIC S9(9)V99.
       01  WS-PAYMENTS-MADE          PIC 9(4) VALUE 0.
       01  WS-TOTAL-INTEREST-PAID    PIC S9(13)V99 VALUE 0.
       01  WS-LOAN-STATUS            PIC X.
           88 LOAN-ACTIVE            VALUE "A".
           88 LOAN-PAID-OFF          VALUE "P".
           88 LOAN-DELINQUENT        VALUE "D".
           88 LOAN-DEFAULT           VALUE "X".
       01  WS-MISSED-PAYMENTS        PIC 9(2) VALUE 0.

       PROCEDURE DIVISION.

      *-----------------------------------------------------------------
      * Method: initLoan
      * Sets up loan-specific parameters and calculates the
      * monthly payment using the standard amortization formula.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. initLoan.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-MONTHLY-RATE           PIC 9V9(10).
       01  WS-POWER-FACTOR           PIC 9(13)V9(10).
       01  WS-NUMERATOR              PIC 9(13)V9(10).
       01  WS-DENOMINATOR            PIC 9(13)V9(10).
       LINKAGE SECTION.
       01  LS-PRINCIPAL              PIC S9(13)V99.
       01  LS-ANNUAL-RATE            PIC 9V9(6).
       01  LS-TERM-MONTHS            PIC 9(4).
       PROCEDURE DIVISION USING LS-PRINCIPAL
                                LS-ANNUAL-RATE
                                LS-TERM-MONTHS.
           MOVE LS-PRINCIPAL TO WS-ORIGINAL-PRINCIPAL
           MOVE LS-ANNUAL-RATE TO WS-INTEREST-RATE
           MOVE LS-TERM-MONTHS TO WS-TERM-MONTHS
           SET LOAN-ACTIVE TO TRUE
           MOVE 0 TO WS-PAYMENTS-MADE
           MOVE 0 TO WS-TOTAL-INTEREST-PAID
           MOVE 0 TO WS-MISSED-PAYMENTS

      *    Calculate monthly payment:
      *    M = P * [r(1+r)^n] / [(1+r)^n - 1]
      *    where r = monthly rate, n = total months
           COMPUTE WS-MONTHLY-RATE =
               WS-INTEREST-RATE / 12
           COMPUTE WS-POWER-FACTOR =
               (1 + WS-MONTHLY-RATE) ** WS-TERM-MONTHS
           COMPUTE WS-NUMERATOR =
               WS-MONTHLY-RATE * WS-POWER-FACTOR
           COMPUTE WS-DENOMINATOR =
               WS-POWER-FACTOR - 1
           COMPUTE WS-MONTHLY-PAYMENT ROUNDED =
               LS-PRINCIPAL * WS-NUMERATOR / WS-DENOMINATOR
           GOBACK.
       END METHOD initLoan.

      *-----------------------------------------------------------------
      * Method: makePayment
      * Processes a loan payment, splitting it between principal
      * and interest based on the current balance.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. makePayment.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CURRENT-BALANCE        PIC S9(13)V99.
       01  WS-INTEREST-PORTION       PIC S9(9)V99.
       01  WS-PRINCIPAL-PORTION      PIC S9(9)V99.
       01  WS-MONTHLY-RATE           PIC 9V9(10).
       01  WS-DEPOSIT-RESULT         PIC 9(2).
       LINKAGE SECTION.
       01  LS-PAYMENT-AMOUNT         PIC S9(9)V99.
       01  LS-INTEREST-APPLIED       PIC S9(9)V99.
       01  LS-PRINCIPAL-APPLIED      PIC S9(9)V99.
       01  LS-REMAINING-BALANCE      PIC S9(13)V99.
       01  LS-RESULT-CODE            PIC 9(2).
           88 PAYMENT-SUCCESS        VALUE 0.
           88 PAYMENT-INVALID        VALUE 1.
           88 PAYMENT-LOAN-CLOSED    VALUE 8.
       PROCEDURE DIVISION USING LS-PAYMENT-AMOUNT
                          RETURNING LS-INTEREST-APPLIED
                                    LS-PRINCIPAL-APPLIED
                                    LS-REMAINING-BALANCE
                                    LS-RESULT-CODE.
           IF LOAN-PAID-OFF
               SET PAYMENT-LOAN-CLOSED TO TRUE
               GOBACK
           END-IF

           IF LS-PAYMENT-AMOUNT <= 0
               SET PAYMENT-INVALID TO TRUE
               GOBACK
           END-IF

      *    Calculate interest and principal portions
           INVOKE SELF "getBalance"
               RETURNING WS-CURRENT-BALANCE

      *    Balance is negative for loans (owed amount)
           COMPUTE WS-MONTHLY-RATE =
               WS-INTEREST-RATE / 12
           COMPUTE WS-INTEREST-PORTION ROUNDED =
               FUNCTION ABS(WS-CURRENT-BALANCE)
               * WS-MONTHLY-RATE

           COMPUTE WS-PRINCIPAL-PORTION =
               LS-PAYMENT-AMOUNT - WS-INTEREST-PORTION

           ADD WS-INTEREST-PORTION TO WS-TOTAL-INTEREST-PAID

      *    Apply payment (deposit increases balance toward zero)
           INVOKE SELF "deposit"
               USING LS-PAYMENT-AMOUNT
               RETURNING WS-DEPOSIT-RESULT

           ADD 1 TO WS-PAYMENTS-MADE
           MOVE 0 TO WS-MISSED-PAYMENTS

           INVOKE SELF "getBalance"
               RETURNING WS-CURRENT-BALANCE

           IF WS-CURRENT-BALANCE >= 0
               SET LOAN-PAID-OFF TO TRUE
           END-IF

           MOVE WS-INTEREST-PORTION TO LS-INTEREST-APPLIED
           MOVE WS-PRINCIPAL-PORTION TO LS-PRINCIPAL-APPLIED
           MOVE WS-CURRENT-BALANCE TO LS-REMAINING-BALANCE
           SET PAYMENT-SUCCESS TO TRUE
           GOBACK.
       END METHOD makePayment.

      *-----------------------------------------------------------------
      * Method: getAmortizationInfo
      * Returns current loan status information.
      *-----------------------------------------------------------------
       IDENTIFICATION DIVISION.
       METHOD-ID. getAmortizationInfo.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CURR-BAL               PIC S9(13)V99.
       LINKAGE SECTION.
       01  LS-LOAN-INFO.
           05  LS-ORIG-PRINCIPAL     PIC S9(13)V99.
           05  LS-CURR-BALANCE       PIC S9(13)V99.
           05  LS-MONTHLY-PMT        PIC S9(9)V99.
           05  LS-PAYMENTS-MADE      PIC 9(4).
           05  LS-PAYMENTS-REMAIN    PIC 9(4).
           05  LS-TOTAL-INT-PAID     PIC S9(13)V99.
           05  LS-STATUS             PIC X.
       PROCEDURE DIVISION RETURNING LS-LOAN-INFO.
           MOVE WS-ORIGINAL-PRINCIPAL TO LS-ORIG-PRINCIPAL
           INVOKE SELF "getBalance"
               RETURNING WS-CURR-BAL
           MOVE WS-CURR-BAL TO LS-CURR-BALANCE
           MOVE WS-MONTHLY-PAYMENT TO LS-MONTHLY-PMT
           MOVE WS-PAYMENTS-MADE TO LS-PAYMENTS-MADE
           COMPUTE LS-PAYMENTS-REMAIN =
               WS-TERM-MONTHS - WS-PAYMENTS-MADE
           MOVE WS-TOTAL-INTEREST-PAID TO LS-TOTAL-INT-PAID
           MOVE WS-LOAN-STATUS TO LS-STATUS
           GOBACK.
       END METHOD getAmortizationInfo.

       END OBJECT.

       END CLASS LoanAccount.

37.10.4 Putting It All Together: Account Portfolio Manager

The following client program demonstrates the full class hierarchy in action, managing a portfolio of different account types:

      ******************************************************************
      * PORTFOLIO MANAGER - Client program
      * Demonstrates polymorphic behavior across the account
      * class hierarchy. Creates multiple account types and
      * processes transactions uniformly.
      * Requires: IBM Enterprise COBOL V4+
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. PortfolioManager.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           CLASS BankAccount      IS "BankAccount"
           CLASS CheckingAccount  IS "CheckingAccount"
           CLASS SavingsAccount   IS "SavingsAccount"
           CLASS LoanAccount      IS "LoanAccount"
           CLASS AccountFactory   IS "AccountFactory".

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-PORTFOLIO.
           05  WS-ACCT-REF OBJECT REFERENCE BankAccount
               OCCURS 10 TIMES.
       01  WS-PORTFOLIO-SIZE         PIC 9(2) VALUE 0.
       01  WS-IDX                    PIC 9(2).
       01  WS-ACCT-TYPE             PIC X(10).
       01  WS-HOLDER-NAME           PIC X(50).
       01  WS-BALANCE               PIC S9(13)V99.
       01  WS-DISPLAY-BAL           PIC -(13)9.99.
       01  WS-ACCT-NUM              PIC 9(10).
       01  WS-RESULT                PIC 9(2).
       01  WS-DEPOSIT-AMT           PIC S9(13)V99.
       01  WS-TOTAL-ASSETS          PIC S9(15)V99 VALUE 0.
       01  WS-DISPLAY-TOTAL         PIC -(15)9.99.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           PERFORM CREATE-ACCOUNTS
           PERFORM PROCESS-TRANSACTIONS
           PERFORM DISPLAY-PORTFOLIO-SUMMARY
           STOP RUN.

       CREATE-ACCOUNTS.
      *    Create a checking account
           MOVE "CHECKING" TO WS-ACCT-TYPE
           MOVE "ALICE JOHNSON" TO WS-HOLDER-NAME
           INVOKE AccountFactory "createAccount"
               USING WS-ACCT-TYPE WS-HOLDER-NAME
               RETURNING WS-ACCT-REF(1)
           MOVE 1 TO WS-PORTFOLIO-SIZE

      *    Create a savings account
           MOVE "SAVINGS" TO WS-ACCT-TYPE
           MOVE "ALICE JOHNSON" TO WS-HOLDER-NAME
           INVOKE AccountFactory "createAccount"
               USING WS-ACCT-TYPE WS-HOLDER-NAME
               RETURNING WS-ACCT-REF(2)
           MOVE 2 TO WS-PORTFOLIO-SIZE

      *    Create a money market account
           MOVE "MONEYMARKET" TO WS-ACCT-TYPE
           MOVE "ALICE JOHNSON" TO WS-HOLDER-NAME
           INVOKE AccountFactory "createAccount"
               USING WS-ACCT-TYPE WS-HOLDER-NAME
               RETURNING WS-ACCT-REF(3)
           MOVE 3 TO WS-PORTFOLIO-SIZE.

       PROCESS-TRANSACTIONS.
      *    Deposit to checking
           MOVE 5000.00 TO WS-DEPOSIT-AMT
           INVOKE WS-ACCT-REF(1) "deposit"
               USING WS-DEPOSIT-AMT
               RETURNING WS-RESULT

      *    Deposit to savings
           MOVE 10000.00 TO WS-DEPOSIT-AMT
           INVOKE WS-ACCT-REF(2) "deposit"
               USING WS-DEPOSIT-AMT
               RETURNING WS-RESULT

      *    Deposit to money market
           MOVE 25000.00 TO WS-DEPOSIT-AMT
           INVOKE WS-ACCT-REF(3) "deposit"
               USING WS-DEPOSIT-AMT
               RETURNING WS-RESULT.

       DISPLAY-PORTFOLIO-SUMMARY.
           DISPLAY "============================================"
           DISPLAY "    PORTFOLIO SUMMARY - ALICE JOHNSON"
           DISPLAY "============================================"
           MOVE 0 TO WS-TOTAL-ASSETS

           PERFORM VARYING WS-IDX FROM 1 BY 1
               UNTIL WS-IDX > WS-PORTFOLIO-SIZE

      *        Polymorphic call - works for any account type
               INVOKE WS-ACCT-REF(WS-IDX) "getAccountNumber"
                   RETURNING WS-ACCT-NUM
               INVOKE WS-ACCT-REF(WS-IDX) "getBalance"
                   RETURNING WS-BALANCE
               MOVE WS-BALANCE TO WS-DISPLAY-BAL
               DISPLAY "Account: " WS-ACCT-NUM
                       "  Balance: $" WS-DISPLAY-BAL
               ADD WS-BALANCE TO WS-TOTAL-ASSETS
           END-PERFORM

           DISPLAY "--------------------------------------------"
           MOVE WS-TOTAL-ASSETS TO WS-DISPLAY-TOTAL
           DISPLAY "Total Portfolio Value: $" WS-DISPLAY-TOTAL
           DISPLAY "============================================".

37.11 Compiler Support

37.11.1 IBM Enterprise COBOL OO Features

IBM Enterprise COBOL for z/OS provides the most comprehensive OO COBOL support among mainframe compilers. Key features and their version availability:

Feature Enterprise COBOL Version
CLASS-ID, METHOD-ID, OBJECT, FACTORY V4.1+
INHERITS clause V4.1+
INTERFACE-ID V4.1+
INVOKE statement V4.1+
Object references V4.1+
SELF and SUPER V4.1+
Method overriding (OVERRIDE) V4.1+
Java interoperability V4.1+ (via OO COBOL-Java bridge)
Multiple interface implementation V4.2+

IBM's implementation requires the Language Environment (LE) runtime and works within the z/OS environment. OO COBOL programs on z/OS can interoperate with Java through the COBOL-Java bridge, allowing COBOL objects to invoke Java methods and vice versa.

IBM Compiler Options for OO COBOL: - THREAD - Required for OO COBOL programs that use Java interoperability - RENT - Reentrant code, required for OO COBOL - DLL - Dynamic link library support, needed for class loading

37.11.2 Micro Focus Visual COBOL

Micro Focus Visual COBOL provides extensive OO support that extends beyond the standard with features specific to its managed code environments:

  • Full COBOL 2002 OO syntax support
  • Integration with .NET Framework (COBOL classes can inherit from .NET classes)
  • Integration with JVM (COBOL classes can be deployed as Java classes)
  • Visual Studio and Eclipse IDE support for OO COBOL development
  • Additional syntax for properties, delegates, and events

37.11.3 GnuCOBOL Limitations

GnuCOBOL (formerly OpenCOBOL), the primary open-source COBOL compiler, does not support OO COBOL features as of version 3.2. The GnuCOBOL project has discussed OO support on its roadmap, but implementation has not been prioritized because:

  1. The majority of GnuCOBOL's user base focuses on migrating and running legacy procedural COBOL programs.
  2. OO COBOL's complexity adds significant compiler development effort.
  3. Organizations using OO COBOL typically operate on IBM mainframes with Enterprise COBOL.

Workarounds for GnuCOBOL Users:

If you are learning on GnuCOBOL and want to explore object-oriented concepts, consider these alternatives:

  • Structured Procedural Design: Use COBOL's COPY, nested programs, and subprogram calling conventions (Chapter 17) to achieve modular, well-encapsulated designs without OO syntax.
  • Module-Based Encapsulation: Create separate COBOL programs for each "class," using a WORKING-STORAGE-based record to represent instance data that is passed between the programs via the CALL statement.
  • External Language Bridge: Use GnuCOBOL's C interoperability to call C or C++ libraries that implement OO logic, with the COBOL program handling business data and the C layer handling object management.

Here is an example of simulating OO concepts in GnuCOBOL using procedural techniques:

      ******************************************************************
      * SIMULATED OO PATTERN FOR GNUCOBOL
      * Uses a "class module" program with operation dispatch
      * to simulate object-oriented behavior.
      *
      * This program acts as both factory and instance manager
      * for BankAccount "objects" represented as data records.
      *
      * Compatible with: GnuCOBOL 2.x and 3.x
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. BankAccountModule.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
      * Instance storage - simulates object pool
       01  WS-MAX-INSTANCES          PIC 9(4) VALUE 100.
       01  WS-INSTANCE-COUNT         PIC 9(4) VALUE 0.
       01  WS-INSTANCE-TABLE.
           05  WS-INSTANCE OCCURS 100 TIMES.
               10  WS-INST-ID        PIC 9(4).
               10  WS-INST-ACCT-NUM  PIC 9(10).
               10  WS-INST-HOLDER    PIC X(50).
               10  WS-INST-BALANCE   PIC S9(13)V99.
               10  WS-INST-TYPE      PIC X(10).
               10  WS-INST-STATUS    PIC 9.
       01  WS-NEXT-ACCT-NUM         PIC 9(10) VALUE 1000000001.

       LINKAGE SECTION.
       01  LS-OPERATION              PIC X(15).
       01  LS-INSTANCE-ID            PIC 9(4).
       01  LS-PARAM-1               PIC X(50).
       01  LS-PARAM-2               PIC X(10).
       01  LS-AMOUNT                PIC S9(13)V99.
       01  LS-RESULT-CODE           PIC 9(2).
       01  LS-RESULT-DATA           PIC X(100).

       PROCEDURE DIVISION USING LS-OPERATION
                                LS-INSTANCE-ID
                                LS-PARAM-1
                                LS-PARAM-2
                                LS-AMOUNT
                                LS-RESULT-CODE
                                LS-RESULT-DATA.
       MAIN-DISPATCH.
           EVALUATE LS-OPERATION
               WHEN "NEW"
                   PERFORM CREATE-INSTANCE
               WHEN "INITIALIZE"
                   PERFORM INIT-INSTANCE
               WHEN "DEPOSIT"
                   PERFORM DEPOSIT-FUNDS
               WHEN "WITHDRAW"
                   PERFORM WITHDRAW-FUNDS
               WHEN "GETBALANCE"
                   PERFORM GET-BALANCE
               WHEN OTHER
                   MOVE 99 TO LS-RESULT-CODE
           END-EVALUATE
           GOBACK.

       CREATE-INSTANCE.
           IF WS-INSTANCE-COUNT >= WS-MAX-INSTANCES
               MOVE 90 TO LS-RESULT-CODE
               GOBACK
           END-IF
           ADD 1 TO WS-INSTANCE-COUNT
           MOVE WS-INSTANCE-COUNT TO LS-INSTANCE-ID
           MOVE WS-INSTANCE-COUNT TO WS-INST-ID(
               WS-INSTANCE-COUNT)
           MOVE 0 TO WS-INST-BALANCE(WS-INSTANCE-COUNT)
           MOVE 1 TO WS-INST-STATUS(WS-INSTANCE-COUNT)
           MOVE 0 TO LS-RESULT-CODE.

       INIT-INSTANCE.
           MOVE LS-PARAM-1 TO
               WS-INST-HOLDER(LS-INSTANCE-ID)
           MOVE LS-PARAM-2 TO
               WS-INST-TYPE(LS-INSTANCE-ID)
           MOVE WS-NEXT-ACCT-NUM TO
               WS-INST-ACCT-NUM(LS-INSTANCE-ID)
           ADD 1 TO WS-NEXT-ACCT-NUM
           MOVE 0 TO LS-RESULT-CODE.

       DEPOSIT-FUNDS.
           IF LS-AMOUNT <= 0
               MOVE 1 TO LS-RESULT-CODE
               GOBACK
           END-IF
           ADD LS-AMOUNT TO
               WS-INST-BALANCE(LS-INSTANCE-ID)
           MOVE 0 TO LS-RESULT-CODE.

       WITHDRAW-FUNDS.
           IF LS-AMOUNT <= 0
               MOVE 1 TO LS-RESULT-CODE
               GOBACK
           END-IF
           IF LS-AMOUNT >
               WS-INST-BALANCE(LS-INSTANCE-ID)
               MOVE 3 TO LS-RESULT-CODE
               GOBACK
           END-IF
           SUBTRACT LS-AMOUNT FROM
               WS-INST-BALANCE(LS-INSTANCE-ID)
           MOVE 0 TO LS-RESULT-CODE.

       GET-BALANCE.
           MOVE WS-INST-BALANCE(LS-INSTANCE-ID)
               TO LS-AMOUNT
           MOVE 0 TO LS-RESULT-CODE.

The calling program would use this module like this:

      * Client program using the simulated OO module
      * Compatible with: GnuCOBOL 2.x and 3.x
       IDENTIFICATION DIVISION.
       PROGRAM-ID. AccountClient.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-OPERATION          PIC X(15).
       01  WS-INST-ID            PIC 9(4).
       01  WS-PARAM-1            PIC X(50).
       01  WS-PARAM-2            PIC X(10).
       01  WS-AMOUNT             PIC S9(13)V99.
       01  WS-RESULT             PIC 9(2).
       01  WS-RESULT-DATA        PIC X(100).
       01  WS-DISPLAY-BAL        PIC -(13)9.99.

       PROCEDURE DIVISION.
       MAIN-LOGIC.
      *    "Create" a new account object
           MOVE "NEW" TO WS-OPERATION
           CALL "BankAccountModule" USING
               WS-OPERATION WS-INST-ID
               WS-PARAM-1 WS-PARAM-2
               WS-AMOUNT WS-RESULT WS-RESULT-DATA

      *    "Initialize" it
           MOVE "INITIALIZE" TO WS-OPERATION
           MOVE "JOHN SMITH" TO WS-PARAM-1
           MOVE "CHECKING" TO WS-PARAM-2
           CALL "BankAccountModule" USING
               WS-OPERATION WS-INST-ID
               WS-PARAM-1 WS-PARAM-2
               WS-AMOUNT WS-RESULT WS-RESULT-DATA

      *    "Deposit" funds
           MOVE "DEPOSIT" TO WS-OPERATION
           MOVE 5000.00 TO WS-AMOUNT
           CALL "BankAccountModule" USING
               WS-OPERATION WS-INST-ID
               WS-PARAM-1 WS-PARAM-2
               WS-AMOUNT WS-RESULT WS-RESULT-DATA

      *    "Get balance"
           MOVE "GETBALANCE" TO WS-OPERATION
           CALL "BankAccountModule" USING
               WS-OPERATION WS-INST-ID
               WS-PARAM-1 WS-PARAM-2
               WS-AMOUNT WS-RESULT WS-RESULT-DATA
           MOVE WS-AMOUNT TO WS-DISPLAY-BAL
           DISPLAY "Balance: $" WS-DISPLAY-BAL

           STOP RUN.

37.12 When to Use OO COBOL vs Traditional Procedural Style

37.12.1 Arguments for OO COBOL

Object-oriented COBOL provides clear advantages in specific scenarios:

Complex Domain Models: When the business domain involves many interrelated entity types with specialized behavior (different account types, different product categories, different regulatory frameworks), OO COBOL's class hierarchies provide a natural way to model these relationships. The inheritance mechanism eliminates code duplication while allowing specialized behavior.

Reusable Components: OO classes are inherently more reusable than procedural programs because they encapsulate both data and behavior. A well-designed BankAccount class can be used across multiple applications without modification, while a procedural program often requires adaptation for each new context.

Team Development: On large teams, OO COBOL's encapsulation reduces the risk of unintended interactions between modules. Each class hides its implementation details behind a method interface, so changes to a class's internal logic do not affect other parts of the system as long as the method signatures remain stable.

Java/C# Interoperability: If your organization is building systems that bridge COBOL and Java or .NET, OO COBOL provides a much smoother integration path. COBOL classes can participate directly in Java class hierarchies on IBM platforms, and in .NET hierarchies on Micro Focus platforms.

37.12.2 Arguments for Procedural COBOL

Traditional procedural COBOL remains the better choice in many situations:

Existing Codebase: If you are maintaining or extending a large existing procedural codebase, introducing OO COBOL creates a mixture of paradigms that can confuse maintainers. Unless there is a compelling architectural reason, consistency with the existing style is usually more valuable than theoretical OO benefits.

Batch Processing: COBOL's traditional strength is high-volume batch processing of sequential files. These programs read records, apply business rules, and write results. The procedural style maps naturally onto this flow, and OO abstraction adds overhead without clear benefit.

Team Skills: Many COBOL programmers have decades of experience with procedural COBOL but limited exposure to OO concepts. Introducing OO COBOL without adequate training can lead to poorly designed class hierarchies that are harder to maintain than the procedural code they replaced.

Performance Sensitivity: OO COBOL introduces runtime overhead for object creation, method dispatch, and garbage collection. In performance-critical batch processing that handles millions of records, this overhead can be meaningful. Procedural COBOL with its direct data manipulation and predictable memory layout remains the fastest option.

Compiler Availability: If your environment uses GnuCOBOL or another compiler without OO support, the question is moot. Procedural COBOL is your only option, and the structured design techniques described in Chapter 17 provide adequate modularization for most applications.

37.12.3 A Balanced Approach

The most pragmatic approach for most organizations is to use procedural COBOL for the core business logic that has been proven over decades, while selectively introducing OO COBOL for new subsystems where the domain complexity justifies it. The techniques in Section 37.9 for mixing procedural and OO code enable this hybrid strategy.

Consider OO COBOL when you are building something genuinely new rather than extending something that already works. Consider procedural COBOL when you are working within an established system and consistency matters more than theoretical elegance.


Summary

Object-oriented COBOL extends the language's capabilities with classes, inheritance, interfaces, and polymorphism while preserving the familiar COBOL syntax and data description strengths. The key concepts covered in this chapter include:

  • CLASS-ID defines a class compilation unit with FACTORY and OBJECT paragraphs.
  • FACTORY contains class-level (shared) data and methods, analogous to static members.
  • OBJECT contains instance-level data and methods, unique to each created object.
  • METHOD-ID defines methods within FACTORY or OBJECT, each with its own divisions.
  • INVOKE calls methods on objects or classes, replacing CALL for OO interactions.
  • INHERITS establishes parent-child class relationships, enabling code reuse.
  • OVERRIDE allows child classes to replace inherited method behavior.
  • SELF and SUPER reference the current object and parent implementation respectively.
  • INTERFACE-ID defines contracts that classes can implement for polymorphic behavior.
  • Design patterns like Factory, Strategy, and Observer adapt well to COBOL's OO features.
  • Mixed paradigm development lets OO and procedural COBOL coexist and interoperate.

The financial domain examples throughout this chapter demonstrate that OO COBOL is not merely a theoretical exercise but a practical tool for modeling complex business relationships. However, the decision to adopt OO COBOL should be driven by genuine architectural needs rather than fashion, and procedural COBOL remains the right choice for many workloads.

In the next chapter, we will explore how COBOL programs -- whether procedural or object-oriented -- integrate with modern APIs, web services, and microservice architectures, bridging the gap between mainframe business logic and contemporary distributed systems.


Review Questions

  1. Explain the difference between FACTORY WORKING-STORAGE and OBJECT WORKING-STORAGE. When would you use each?

  2. Write a CLASS-ID paragraph for a CreditCardAccount class that inherits from BankAccount and implements both InterestBearing and FeeChargeable interfaces.

  3. Describe the difference between INVOKE SELF and INVOKE SUPER. Provide a scenario where using the wrong one would produce incorrect results.

  4. Why does GnuCOBOL not support OO COBOL features? What procedural techniques can you use as alternatives?

  5. In the Factory design pattern example, why does the createAccount method return an OBJECT REFERENCE BankAccount rather than a reference to the specific account type?

  6. When migrating a procedural COBOL codebase to OO, why is a phased approach recommended over a complete rewrite? What risks does a complete rewrite introduce?

  7. Explain how interface-based polymorphism allows a utility program to process different types of accounts without knowing their specific class.

  8. Compare the memory allocation behavior of procedural COBOL WORKING-STORAGE with OO COBOL's per-instance data. How does this affect programs that process millions of records?


Exercises

Exercise 37.1: Define a CertificateOfDeposit class that inherits from SavingsAccount. It should have a maturity date, an early withdrawal penalty rate (e.g., 10% of interest earned), and a method that calculates the penalty for early withdrawal based on how many months remain until maturity.

Exercise 37.2: Implement the FeeChargeable interface with methods calculateFees, assessFees, and waiveFees. Then implement this interface in the CheckingAccount class.

Exercise 37.3: Create a TransactionLog observer class that implements AccountObserver and writes all transaction notifications to a sequential file. Each record should include the account number, transaction type, amount, new balance, and a timestamp.

Exercise 37.4: Using the GnuCOBOL-compatible procedural simulation pattern from Section 37.11.3, implement a SavingsAccountModule that adds interest calculation to the base BankAccountModule pattern. Demonstrate how procedural code can achieve method-override-like behavior through operation dispatch tables.

Exercise 37.5: Design and implement a CustomerPortfolio class that holds references to multiple BankAccount objects (of any subtype) and provides methods to calculate total assets, total liabilities (from LoanAccounts), and net worth. Demonstrate polymorphic iteration over the portfolio's accounts.