> "The object-oriented paradigm isn't a replacement for COBOL's strengths — it's an extension of them. The question isn't whether COBOL can be object-oriented; it's whether your shop should adopt OO COBOL, and how far along the spectrum to go." —...
In This Chapter
- 25.1 A Brief History of OO COBOL
- 25.2 The CLASS-ID Paragraph — Defining Classes
- 25.3 The OBJECT and FACTORY Sections
- 25.4 METHOD-ID — Defining Methods
- 25.5 The INVOKE Statement — Calling Methods
- 25.14 Comparing OO COBOL to OO in Other Languages
- 25.15 Best Practices for OO COBOL
- 25.16 The LoanAccount Subclass — A Complete Worked Example
- 25.17 Error Handling in OO COBOL
- 25.18 The Student Mainframe Lab: Experimenting with OO COBOL
- 25.19 Chapter Review Questions
- 25.20 Exception Handling and the ON EXCEPTION Clause
- 25.21 OO COBOL in the Broader Technology Landscape
- 25.22 Summary
Chapter 25: Object-Oriented COBOL
"The object-oriented paradigm isn't a replacement for COBOL's strengths — it's an extension of them. The question isn't whether COBOL can be object-oriented; it's whether your shop should adopt OO COBOL, and how far along the spectrum to go." — Priya Kapoor, systems architect, GlobalBank
When Derek Washington joined GlobalBank fresh from a computer science program, he carried assumptions about what "real" object-oriented programming looked like. Java classes, Python objects, C++ templates — these were the tools of modern development. So when Priya Kapoor mentioned that COBOL had supported object-oriented programming since 2002, Derek's first reaction was skepticism. "Object-oriented COBOL?" he asked. "That sounds like putting a spoiler on a tractor."
Maria Chen, overhearing from the next desk, smiled. "I thought the same thing fifteen years ago," she said. "Then I saw what Micro Focus did with their OO COBOL compiler on a modernization project. It wasn't about making COBOL into Java. It was about giving COBOL developers a way to organize complex systems without throwing away fifty years of working code."
This chapter explores one of COBOL's most ambitious — and most debated — extensions: object-oriented programming. We will examine the syntax introduced by the COBOL 2002 standard, understand the conceptual model that OO COBOL provides, work through practical examples, and confront the real-world challenges that have limited OO COBOL adoption. By the end, you will be equipped to read, understand, and write OO COBOL programs, and more importantly, to make informed decisions about when and whether to use these capabilities.
25.1 A Brief History of OO COBOL
The journey toward object-oriented COBOL began in the early 1990s, when the object-oriented paradigm was sweeping through the software industry. Smalltalk, C++, and eventually Java demonstrated that organizing code around objects — data bundled with the operations that act on it — could tame complexity in large systems.
The COBOL community watched this revolution with a mix of interest and wariness. COBOL already excelled at what it did: processing business data in structured, readable programs. But as systems grew larger and more complex, COBOL shops faced the same organizational challenges that OO was designed to address.
The Standards Timeline
The path to OO COBOL followed this trajectory:
- 1993–1997: The ISO/ANSI COBOL committee began work on object-oriented extensions. Micro Focus and IBM contributed competing proposals.
- 1997: An Object-Oriented COBOL draft was circulated, drawing heavily on Smalltalk concepts.
- 2002: The COBOL 2002 standard (ISO/IEC 1989:2002) was ratified, including full OO extensions — classes, methods, inheritance, interfaces, and object references.
- 2014: The COBOL 2014 standard refined OO features and added additional capabilities.
- Present day: OO COBOL is supported by Micro Focus COBOL (now Rocket Software), IBM Enterprise COBOL (partial support), and GnuCOBOL (limited support).
💡 Key Insight: The COBOL 2002 standard didn't just bolt objects onto COBOL. It defined a complete object model, including factories (class-level behavior), interfaces, and parameterized methods. The model is closer to Smalltalk than to C++ or Java, though the syntax is unmistakably COBOL.
Adoption Reality
Here is the uncomfortable truth that any honest textbook must acknowledge: OO COBOL has seen limited adoption in production environments. Several factors explain this:
- Compiler support varies widely. Micro Focus provides the most complete implementation. IBM Enterprise COBOL supports a subset. GnuCOBOL support is minimal.
- Existing codebases are procedural. Introducing OO constructs into a million-line procedural codebase requires careful planning and significant training investment.
- The COBOL workforce is procedural. Most COBOL developers learned structured programming, not OO design. Training costs are real.
- Java and C# filled the OO niche. Many shops that wanted OO design chose to write new components in Java rather than adopt OO COBOL.
⚖️ The Modernization Spectrum: OO COBOL sits at a fascinating position on the modernization spectrum. It offers genuine architectural improvement while staying within the COBOL ecosystem — no language migration, no retraining on entirely new platforms. But its limited adoption means fewer examples, fewer community resources, and fewer developers who know it. This is a classic technology adoption dilemma.
25.2 The CLASS-ID Paragraph — Defining Classes
In procedural COBOL, the fundamental organizational unit is the program, identified by PROGRAM-ID. In OO COBOL, we add a new fundamental unit: the class, identified by CLASS-ID.
A class definition replaces the PROGRAM-ID paragraph with a CLASS-ID paragraph and introduces a fundamentally different program structure.
Basic Class Structure
IDENTIFICATION DIVISION.
CLASS-ID. BankAccount.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS BankAccount IS "BankAccount".
DATA DIVISION.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ACCOUNT-NUMBER PIC X(10).
01 WS-ACCOUNT-HOLDER PIC X(40).
01 WS-BALANCE PIC S9(11)V99 VALUE ZEROS.
01 WS-ACCOUNT-STATUS PIC X VALUE 'A'.
88 ACCT-ACTIVE VALUE 'A'.
88 ACCT-CLOSED VALUE 'C'.
88 ACCT-FROZEN VALUE 'F'.
PROCEDURE DIVISION.
METHOD-ID. GetBalance.
DATA DIVISION.
LINKAGE SECTION.
01 LS-BALANCE PIC S9(11)V99.
PROCEDURE DIVISION RETURNING LS-BALANCE.
MOVE WS-BALANCE TO LS-BALANCE.
END METHOD GetBalance.
METHOD-ID. Deposit.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-RESULT-CODE PIC 9 VALUE 0.
LINKAGE SECTION.
01 LS-AMOUNT PIC S9(11)V99.
01 LS-RETURN-CODE PIC 9.
PROCEDURE DIVISION USING LS-AMOUNT
RETURNING LS-RETURN-CODE.
IF LS-AMOUNT > ZEROS
ADD LS-AMOUNT TO WS-BALANCE
MOVE 0 TO LS-RETURN-CODE
ELSE
MOVE 1 TO LS-RETURN-CODE
END-IF.
END METHOD Deposit.
END OBJECT.
END CLASS BankAccount.
Let us dissect this structure piece by piece.
CLASS-ID Paragraph
CLASS-ID. BankAccount.
This replaces PROGRAM-ID and tells the compiler that this source unit defines a class, not a standalone program. The class name follows COBOL naming conventions — up to 30 characters, letters, digits, and hyphens.
📊 Comparison to Java: Where Java writes public class BankAccount { ... }, COBOL writes CLASS-ID. BankAccount. followed by the class body, terminated by END CLASS BankAccount. This is more verbose, but it follows COBOL's longstanding pattern of explicit, self-documenting structure.
The REPOSITORY Paragraph
The REPOSITORY paragraph in the ENVIRONMENT DIVISION serves as a class registry. It declares which classes this source unit knows about, mapping logical class names to their physical implementations.
REPOSITORY.
CLASS BankAccount IS "BankAccount"
CLASS CheckingAccount IS "CheckingAccount"
CLASS SavingsAccount IS "SavingsAccount".
Think of the REPOSITORY as the OO COBOL equivalent of Java's import statements. It tells the compiler where to find class definitions that this class references. The quoted string on the right is the external class name — typically the name used by the runtime to locate the compiled class.
⚠️ Compiler Variation: The exact format of the REPOSITORY paragraph varies between compilers. Micro Focus uses one convention, IBM another. Always consult your compiler's documentation for the correct syntax. The examples in this chapter follow the COBOL 2002 standard syntax.
25.3 The OBJECT and FACTORY Sections
OO COBOL classes have two distinct sections that contain data and methods: the OBJECT section and the FACTORY section. Understanding the difference between them is crucial.
The OBJECT Section
The OBJECT section defines instance data and instance methods — things that belong to each individual object created from the class.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ACCOUNT-NUMBER PIC X(10).
01 WS-BALANCE PIC S9(11)V99 VALUE ZEROS.
PROCEDURE DIVISION.
METHOD-ID. GetBalance.
...
END METHOD GetBalance.
END OBJECT.
Every time you create a new BankAccount object, it gets its own copy of WS-ACCOUNT-NUMBER and WS-BALANCE. The methods defined in the OBJECT section operate on that specific object's data.
The FACTORY Section
The FACTORY section defines class-level data and class-level methods. These are shared across all instances and can be called without creating an object first.
FACTORY.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TOTAL-ACCOUNTS PIC 9(7) VALUE ZEROS.
01 WS-NEXT-ACCOUNT-ID PIC 9(10) VALUE 1.
PROCEDURE DIVISION.
METHOD-ID. CreateAccount.
DATA DIVISION.
LINKAGE SECTION.
01 LS-HOLDER-NAME PIC X(40).
01 LS-NEW-ACCOUNT USAGE OBJECT REFERENCE
BankAccount.
PROCEDURE DIVISION USING LS-HOLDER-NAME
RETURNING LS-NEW-ACCOUNT.
INVOKE BankAccount "NEW"
RETURNING LS-NEW-ACCOUNT
INVOKE LS-NEW-ACCOUNT "Initialize"
USING WS-NEXT-ACCOUNT-ID
LS-HOLDER-NAME
ADD 1 TO WS-NEXT-ACCOUNT-ID
ADD 1 TO WS-TOTAL-ACCOUNTS.
END METHOD CreateAccount.
METHOD-ID. GetTotalAccounts.
DATA DIVISION.
LINKAGE SECTION.
01 LS-COUNT PIC 9(7).
PROCEDURE DIVISION RETURNING LS-COUNT.
MOVE WS-TOTAL-ACCOUNTS TO LS-COUNT.
END METHOD GetTotalAccounts.
END FACTORY.
📊 Comparison to Java: The FACTORY section is analogous to static members in Java. Factory data is like a static field; factory methods are like static methods. The CreateAccount factory method above is a classic factory pattern — it creates and initializes new objects, encapsulating the creation logic.
💡 Key Insight: The "NEW" method is a built-in factory method provided by the OO COBOL runtime. When you INVOKE a class with "NEW", the runtime allocates memory for a new object and returns a reference to it. You then initialize the object's instance data through your own methods.
Complete Class with Both Sections
Here is a complete BankAccount class with both FACTORY and OBJECT sections:
IDENTIFICATION DIVISION.
CLASS-ID. BankAccount.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS BankAccount IS "BankAccount".
FACTORY.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TOTAL-ACCOUNTS PIC 9(7) VALUE ZEROS.
01 WS-NEXT-ACCT-NUM PIC 9(10) VALUE 1000000001.
PROCEDURE DIVISION.
METHOD-ID. CreateAccount.
DATA DIVISION.
LINKAGE SECTION.
01 LS-HOLDER-NAME PIC X(40).
01 LS-INITIAL-BALANCE PIC S9(11)V99.
01 LS-NEW-ACCOUNT USAGE OBJECT REFERENCE
BankAccount.
PROCEDURE DIVISION USING LS-HOLDER-NAME
LS-INITIAL-BALANCE
RETURNING LS-NEW-ACCOUNT.
INVOKE BankAccount "NEW"
RETURNING LS-NEW-ACCOUNT
INVOKE LS-NEW-ACCOUNT "Initialize"
USING WS-NEXT-ACCT-NUM
LS-HOLDER-NAME
LS-INITIAL-BALANCE
ADD 1 TO WS-NEXT-ACCT-NUM
ADD 1 TO WS-TOTAL-ACCOUNTS.
END METHOD CreateAccount.
METHOD-ID. GetTotalAccounts.
DATA DIVISION.
LINKAGE SECTION.
01 LS-TOTAL PIC 9(7).
PROCEDURE DIVISION RETURNING LS-TOTAL.
MOVE WS-TOTAL-ACCOUNTS TO LS-TOTAL.
END METHOD GetTotalAccounts.
END FACTORY.
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ACCOUNT-NUMBER PIC 9(10).
01 WS-ACCOUNT-HOLDER PIC X(40).
01 WS-BALANCE PIC S9(11)V99 VALUE ZEROS.
01 WS-STATUS PIC X VALUE 'A'.
88 ACCT-ACTIVE VALUE 'A'.
88 ACCT-CLOSED VALUE 'C'.
88 ACCT-FROZEN VALUE 'F'.
01 WS-OPEN-DATE PIC 9(8).
01 WS-TXN-COUNT PIC 9(7) VALUE ZEROS.
PROCEDURE DIVISION.
METHOD-ID. Initialize.
DATA DIVISION.
LINKAGE SECTION.
01 LS-ACCT-NUM PIC 9(10).
01 LS-HOLDER PIC X(40).
01 LS-INIT-BAL PIC S9(11)V99.
PROCEDURE DIVISION USING LS-ACCT-NUM
LS-HOLDER
LS-INIT-BAL.
MOVE LS-ACCT-NUM TO WS-ACCOUNT-NUMBER
MOVE LS-HOLDER TO WS-ACCOUNT-HOLDER
MOVE LS-INIT-BAL TO WS-BALANCE
SET ACCT-ACTIVE TO TRUE
ACCEPT WS-OPEN-DATE FROM DATE YYYYMMDD
MOVE ZEROS TO WS-TXN-COUNT.
END METHOD Initialize.
METHOD-ID. Deposit.
DATA DIVISION.
LINKAGE SECTION.
01 LS-AMOUNT PIC S9(11)V99.
01 LS-RESULT PIC 9.
PROCEDURE DIVISION USING LS-AMOUNT
RETURNING LS-RESULT.
EVALUATE TRUE
WHEN ACCT-ACTIVE
IF LS-AMOUNT > ZEROS
ADD LS-AMOUNT TO WS-BALANCE
ADD 1 TO WS-TXN-COUNT
MOVE 0 TO LS-RESULT
ELSE
MOVE 1 TO LS-RESULT
END-IF
WHEN ACCT-FROZEN
MOVE 2 TO LS-RESULT
WHEN ACCT-CLOSED
MOVE 3 TO LS-RESULT
END-EVALUATE.
END METHOD Deposit.
METHOD-ID. Withdraw.
DATA DIVISION.
LINKAGE SECTION.
01 LS-AMOUNT PIC S9(11)V99.
01 LS-RESULT PIC 9.
PROCEDURE DIVISION USING LS-AMOUNT
RETURNING LS-RESULT.
EVALUATE TRUE
WHEN ACCT-ACTIVE
IF LS-AMOUNT > ZEROS
AND LS-AMOUNT <= WS-BALANCE
SUBTRACT LS-AMOUNT FROM WS-BALANCE
ADD 1 TO WS-TXN-COUNT
MOVE 0 TO LS-RESULT
ELSE
IF LS-AMOUNT > WS-BALANCE
MOVE 4 TO LS-RESULT
ELSE
MOVE 1 TO LS-RESULT
END-IF
END-IF
WHEN ACCT-FROZEN
MOVE 2 TO LS-RESULT
WHEN ACCT-CLOSED
MOVE 3 TO LS-RESULT
END-EVALUATE.
END METHOD Withdraw.
METHOD-ID. GetBalance.
DATA DIVISION.
LINKAGE SECTION.
01 LS-BALANCE PIC S9(11)V99.
PROCEDURE DIVISION RETURNING LS-BALANCE.
MOVE WS-BALANCE TO LS-BALANCE.
END METHOD GetBalance.
METHOD-ID. GetAccountInfo.
DATA DIVISION.
LINKAGE SECTION.
01 LS-ACCT-NUM PIC 9(10).
01 LS-HOLDER PIC X(40).
01 LS-STATUS PIC X.
PROCEDURE DIVISION RETURNING LS-ACCT-NUM
LS-HOLDER
LS-STATUS.
MOVE WS-ACCOUNT-NUMBER TO LS-ACCT-NUM
MOVE WS-ACCOUNT-HOLDER TO LS-HOLDER
MOVE WS-STATUS TO LS-STATUS.
END METHOD GetAccountInfo.
END OBJECT.
END CLASS BankAccount.
25.4 METHOD-ID — Defining Methods
Methods are the workhorses of OO COBOL. Each method is a self-contained unit with its own DATA DIVISION and PROCEDURE DIVISION, much like an inner program — but with access to the object's (or factory's) instance data.
Method Structure
Every method follows this pattern:
METHOD-ID. MethodName.
DATA DIVISION.
WORKING-STORAGE SECTION.
*> Local variables (private to this method invocation)
01 WS-LOCAL-VAR PIC X(10).
LINKAGE SECTION.
*> Parameters and return values
01 LS-PARAM-1 PIC X(20).
01 LS-RETURN-VAL PIC 9(5).
PROCEDURE DIVISION USING LS-PARAM-1
RETURNING LS-RETURN-VAL.
*> Method body
MOVE SPACES TO WS-LOCAL-VAR
COMPUTE LS-RETURN-VAL = FUNCTION LENGTH(LS-PARAM-1)
.
END METHOD MethodName.
Parameter Passing in Methods
Methods support the same parameter-passing conventions as traditional COBOL programs, with some OO-specific additions:
METHOD-ID. TransferFunds.
DATA DIVISION.
LINKAGE SECTION.
01 LS-TARGET-ACCT USAGE OBJECT REFERENCE
BankAccount.
01 LS-AMOUNT PIC S9(11)V99.
01 LS-RESULT PIC 9.
PROCEDURE DIVISION USING LS-TARGET-ACCT
LS-AMOUNT
RETURNING LS-RESULT.
*> Withdraw from this account
INVOKE SELF "Withdraw"
USING LS-AMOUNT
RETURNING LS-RESULT
IF LS-RESULT = 0
*> Deposit to target account
INVOKE LS-TARGET-ACCT "Deposit"
USING LS-AMOUNT
RETURNING LS-RESULT
IF LS-RESULT NOT = 0
*> Rollback: re-deposit to this account
INVOKE SELF "Deposit"
USING LS-AMOUNT
RETURNING LS-RESULT
MOVE 5 TO LS-RESULT
END-IF
END-IF.
END METHOD TransferFunds.
Notice the use of SELF — this is the OO COBOL keyword that refers to the current object, equivalent to this in Java or C++. When you write INVOKE SELF "Withdraw", you are calling the Withdraw method on the same object that TransferFunds was called on.
💡 Key Insight: Methods have their own WORKING-STORAGE, which is local to each method invocation. But they also have implicit access to the instance data declared in the OBJECT section's WORKING-STORAGE. This is how methods access and modify the object's state. The method's local WORKING-STORAGE is allocated fresh for each invocation, while the object's WORKING-STORAGE persists for the object's lifetime.
25.5 The INVOKE Statement — Calling Methods
The INVOKE statement is the mechanism for calling methods on objects. It is the OO COBOL equivalent of the dot operator in Java (object.method()) or the arrow operator in C++ (object->method()).
Basic INVOKE Syntax
*> Call a method with no parameters
INVOKE myAccount "GetBalance"
RETURNING WS-BALANCE
*> Call a method with parameters
INVOKE myAccount "Deposit"
USING WS-DEPOSIT-AMOUNT
RETURNING WS-RESULT-CODE
*> Call a factory method (on the class itself)
INVOKE BankAccount "CreateAccount"
USING WS-HOLDER-NAME
WS-INITIAL-BALANCE
RETURNING myAccount
INVOKE Variations
OO COBOL provides several forms of the INVOKE statement:
*> Invoke on an object reference
INVOKE objRef "MethodName" USING param1
*> Invoke on SELF (current object)
INVOKE SELF "MethodName" USING param1
*> Invoke on SUPER (parent class method)
INVOKE SUPER "MethodName" USING param1
*> Invoke a factory method (class method)
INVOKE ClassName "MethodName" USING param1
*> Invoke NEW to create an object
INVOKE ClassName "NEW" RETURNING objRef
The SUPER keyword is particularly important for inheritance, which we will cover in Section 25.7. When you INVOKE SUPER, you call the parent class's version of a method, even if the current class has overridden it.
🧪 Try It Yourself: A Simple Client Program
Here is a complete client program that uses the BankAccount class. If you have access to a Micro Focus COBOL compiler, you can compile and run this:
IDENTIFICATION DIVISION.
PROGRAM-ID. AccountDemo.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS BankAccount IS "BankAccount".
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ACCOUNT-1 USAGE OBJECT REFERENCE
BankAccount.
01 WS-ACCOUNT-2 USAGE OBJECT REFERENCE
BankAccount.
01 WS-BALANCE PIC S9(11)V99.
01 WS-RESULT PIC 9.
01 WS-TOTAL-ACCTS PIC 9(7).
01 WS-DISPLAY-BAL PIC $$$,$$$, MATH1 $,$$$,$$9.99-.
01 WS-RESULT PIC 9.
01 WS-SUCCESS PIC X.
01 WS-INDEX PIC 9(4).
01 WS-COLL-SIZE PIC 9(4).
01 WS-TOTAL-BAL PIC S9(13)V99.
01 WS-DISPLAY-TOTAL PIC $$$,$$$,$$$,$$9.99-.
01 WS-FEE-CHARGED PIC S9(7)V99.
01 WS-INTEREST-PAID PIC S9(9)V99.
01 WS-DISPLAY-AMT PIC $$$,$$$,$$9.99-.
PROCEDURE DIVISION.
MAIN-LOGIC.
DISPLAY "=== END OF DAY PROCESSING ==="
DISPLAY " "
*> Create collection
INVOKE AccountCollection "NEW"
RETURNING WS-ALL-ACCOUNTS
*> Create and populate accounts
PERFORM CREATE-CHECKING-ACCOUNTS
PERFORM CREATE-SAVINGS-ACCOUNTS
*> Process all accounts
PERFORM PROCESS-ALL-ACCOUNTS
*> Report totals
PERFORM REPORT-TOTALS
DISPLAY " "
DISPLAY "=== PROCESSING COMPLETE ==="
STOP RUN.
CREATE-CHECKING-ACCOUNTS.
INVOKE CheckingAccount "NEW"
RETURNING WS-CHECKING-1
INVOKE WS-CHECKING-1 "Initialize"
USING 1000000001
"Maria Chen"
15420.67
INVOKE WS-CHECKING-1 "SetOverdraftLimit"
USING 5000.00
SET WS-GENERIC-ACCT TO WS-CHECKING-1
INVOKE WS-ALL-ACCOUNTS "Add"
USING WS-GENERIC-ACCT
RETURNING WS-SUCCESS.
CREATE-SAVINGS-ACCOUNTS.
INVOKE SavingsAccount "NEW"
RETURNING WS-SAVINGS-1
INVOKE WS-SAVINGS-1 "Initialize"
USING 2000000001
"Maria Chen"
52000.00
SET WS-GENERIC-ACCT TO WS-SAVINGS-1
INVOKE WS-ALL-ACCOUNTS "Add"
USING WS-GENERIC-ACCT
RETURNING WS-SUCCESS.
PROCESS-ALL-ACCOUNTS.
INVOKE WS-ALL-ACCOUNTS "GetSize"
RETURNING WS-COLL-SIZE
DISPLAY "Processing " WS-COLL-SIZE " accounts..."
DISPLAY " "
PERFORM VARYING WS-INDEX FROM 1 BY 1
UNTIL WS-INDEX > WS-COLL-SIZE
INVOKE WS-ALL-ACCOUNTS "GetAt"
USING WS-INDEX
RETURNING WS-GENERIC-ACCT
INVOKE WS-GENERIC-ACCT "GetBalance"
RETURNING WS-BALANCE
MOVE WS-BALANCE TO WS-DISPLAY-BAL
DISPLAY "Account #" WS-INDEX
" Balance: " WS-DISPLAY-BAL
END-PERFORM.
REPORT-TOTALS.
INVOKE WS-ALL-ACCOUNTS "TotalBalance"
RETURNING WS-TOTAL-BAL
MOVE WS-TOTAL-BAL TO WS-DISPLAY-TOTAL
DISPLAY " "
DISPLAY "Total assets under management: "
WS-DISPLAY-TOTAL.
25.14 Comparing OO COBOL to OO in Other Languages
For students who know Java, C#, or Python, here is a side-by-side comparison:
| Concept | Java | OO COBOL |
|---|---|---|
| Class declaration | class Foo { } |
CLASS-ID. Foo. ... END CLASS Foo. |
| Constructor | new Foo() |
INVOKE Foo "NEW" |
| Method call | obj.method(args) |
INVOKE obj "method" USING args |
| This reference | this |
SELF |
| Parent call | super.method() |
INVOKE SUPER "method" |
| Extends | class Bar extends Foo |
CLASS-ID. Bar INHERITS Foo. |
| Implements | class Bar implements I |
CLASS-ID. Bar IMPLEMENTS I. |
| Interface | interface I { } |
INTERFACE-ID. I. ... END INTERFACE I. |
| Static method | static void m() |
FACTORY. METHOD-ID. m. |
| Null reference | null |
NULL |
| Type check | instanceof |
Not directly supported |
📊 Observation: OO COBOL is more verbose than Java for every construct. A Java class that takes 30 lines might take 80 lines in OO COBOL. But COBOL has always traded brevity for explicitness. The question is whether the explicitness adds clarity or just noise — and the answer depends on your team and context.
25.15 Best Practices for OO COBOL
If you do adopt OO COBOL, here are guidelines drawn from real-world experience:
1. Start Small
Do not attempt to convert an entire system to OO at once. Begin with one well-defined domain area — a new subsystem or a module being rewritten anyway.
2. Use Shallow Hierarchies
Deep inheritance trees (five or more levels) are hard to understand and debug in any language. In COBOL, where runtime debugging tools may have limited OO support, keep inheritance to two or three levels.
3. Prefer Composition over Inheritance
Instead of creating deep class hierarchies, use composition — have objects contain references to other objects:
01 WS-STATEMENT-PRINTER USAGE OBJECT REFERENCE
StatementPrinter.
01 WS-AUDIT-LOGGER USAGE OBJECT REFERENCE
AuditLogger.
4. Design Methods Around Business Operations
Name methods after business operations, not technical operations:
*> Good — business-meaningful
METHOD-ID. ProcessEndOfDaySettlement.
METHOD-ID. CalculateMonthlyInterest.
METHOD-ID. ApplyRegulatoryHold.
*> Less good — technical
METHOD-ID. UpdateField3.
METHOD-ID. SetFlag.
METHOD-ID. RunCalc1.
5. Document Compiler Requirements
Every OO COBOL module should document which compiler and version it requires:
*> --------------------------------------------------------
*> Requires: Micro Focus Visual COBOL 6.0+
*> OO Features: CLASS-ID, FACTORY, INHERITS, INTERFACES
*> Not compatible with: IBM Enterprise COBOL < 6.3
*> GnuCOBOL (any version)
*> --------------------------------------------------------
6. Provide Procedural Wrappers
For gradual adoption, create procedural wrapper programs that expose OO functionality to legacy callers:
IDENTIFICATION DIVISION.
PROGRAM-ID. ACCT-CREATE-WRAPPER.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS BankAccount IS "BankAccount".
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ACCT-REF USAGE OBJECT REFERENCE
BankAccount.
LINKAGE SECTION.
01 LS-HOLDER-NAME PIC X(40).
01 LS-INITIAL-BALANCE PIC S9(11)V99.
01 LS-RETURN-CODE PIC 9.
PROCEDURE DIVISION USING LS-HOLDER-NAME
LS-INITIAL-BALANCE
LS-RETURN-CODE.
INVOKE BankAccount "CreateAccount"
USING LS-HOLDER-NAME
LS-INITIAL-BALANCE
RETURNING WS-ACCT-REF
IF WS-ACCT-REF NOT = NULL
MOVE 0 TO LS-RETURN-CODE
ELSE
MOVE 1 TO LS-RETURN-CODE
END-IF
STOP RUN.
This pattern lets existing CALL-based programs interact with new OO classes without any changes to the calling programs.
25.16 The LoanAccount Subclass — A Complete Worked Example
To complete our GlobalBank account hierarchy, let us build the LoanAccount class from scratch. This is a more complex example that demonstrates how inheritance can model genuinely different behavior — a loan account's fundamental operations are the inverse of a deposit account's.
Design Considerations
A loan account differs from checking and savings accounts in several important ways:
- The balance represents money owed, not money held. A "higher balance" is worse for the customer, not better.
- Deposits reduce the balance (payments against the loan). Withdrawals do not make sense in the same way — you cannot withdraw from a loan.
- Interest works against the customer, increasing the balance rather than adding to it.
- The account has a term — a defined number of months over which the loan must be repaid.
These differences mean we need to think carefully about which methods to inherit, which to override, and which to add as new.
IDENTIFICATION DIVISION.
CLASS-ID. LoanAccount
INHERITS BankAccount.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
CLASS BankAccount IS "BankAccount"
CLASS LoanAccount IS "LoanAccount".
OBJECT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ORIGINAL-PRINCIPAL PIC S9(11)V99.
01 WS-ANNUAL-RATE PIC V9(6).
01 WS-MONTHLY-RATE PIC V9(8).
01 WS-TERM-MONTHS PIC 9(4).
01 WS-REMAINING-MONTHS PIC 9(4).
01 WS-MONTHLY-PAYMENT PIC S9(9)V99.
01 WS-TOTAL-INTEREST PIC S9(11)V99 VALUE ZEROS.
01 WS-LOAN-STATUS PIC X VALUE 'A'.
88 LOAN-ACTIVE VALUE 'A'.
88 LOAN-PAID-OFF VALUE 'P'.
88 LOAN-DEFAULTED VALUE 'D'.
88 LOAN-DEFERRED VALUE 'F'.
PROCEDURE DIVISION.
METHOD-ID. InitializeLoan.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-RATE-FACTOR PIC S9V9(8).
01 WS-NUMERATOR PIC S9(13)V9(4).
01 WS-DENOMINATOR PIC S9(5)V9(8).
LINKAGE SECTION.
01 LS-PRINCIPAL PIC S9(11)V99.
01 LS-ANNUAL-RATE PIC V9(6).
01 LS-TERM PIC 9(4).
PROCEDURE DIVISION USING LS-PRINCIPAL
LS-ANNUAL-RATE
LS-TERM.
MOVE LS-PRINCIPAL TO WS-ORIGINAL-PRINCIPAL
MOVE LS-ANNUAL-RATE TO WS-ANNUAL-RATE
MOVE LS-TERM TO WS-TERM-MONTHS
MOVE LS-TERM TO WS-REMAINING-MONTHS
COMPUTE WS-MONTHLY-RATE =
WS-ANNUAL-RATE / 12
*> Calculate monthly payment using amortization
*> formula: P * [r(1+r)^n] / [(1+r)^n - 1]
COMPUTE WS-RATE-FACTOR =
(1 + WS-MONTHLY-RATE)
** WS-TERM-MONTHS
COMPUTE WS-NUMERATOR =
LS-PRINCIPAL * WS-MONTHLY-RATE
* WS-RATE-FACTOR
COMPUTE WS-DENOMINATOR =
WS-RATE-FACTOR - 1
COMPUTE WS-MONTHLY-PAYMENT ROUNDED =
WS-NUMERATOR / WS-DENOMINATOR
SET LOAN-ACTIVE TO TRUE
MOVE ZEROS TO WS-TOTAL-INTEREST.
END METHOD InitializeLoan.
METHOD-ID. Withdraw OVERRIDE.
*> Loans do not support withdrawals
DATA DIVISION.
LINKAGE SECTION.
01 LS-AMOUNT PIC S9(11)V99.
01 LS-RESULT PIC 9.
PROCEDURE DIVISION USING LS-AMOUNT
RETURNING LS-RESULT.
MOVE 7 TO LS-RESULT.
END METHOD Withdraw.
METHOD-ID. MakePayment.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CURRENT-BAL PIC S9(11)V99.
01 WS-INTEREST-PORTION PIC S9(9)V99.
01 WS-PRINCIPAL-PORTION PIC S9(9)V99.
01 WS-DEPOSIT-RESULT PIC 9.
LINKAGE SECTION.
01 LS-PAYMENT PIC S9(9)V99.
01 LS-INTEREST-APPLIED PIC S9(9)V99.
01 LS-PRINCIPAL-APPLIED PIC S9(9)V99.
01 LS-RESULT PIC 9.
PROCEDURE DIVISION USING LS-PAYMENT
RETURNING LS-INTEREST-APPLIED
LS-PRINCIPAL-APPLIED
LS-RESULT.
IF NOT LOAN-ACTIVE
MOVE 8 TO LS-RESULT
GOBACK
END-IF
IF LS-PAYMENT <= ZEROS
MOVE 1 TO LS-RESULT
GOBACK
END-IF
*> Calculate interest portion of payment
INVOKE SELF "GetBalance"
RETURNING WS-CURRENT-BAL
COMPUTE WS-INTEREST-PORTION ROUNDED =
WS-CURRENT-BAL * WS-MONTHLY-RATE
COMPUTE WS-PRINCIPAL-PORTION =
LS-PAYMENT - WS-INTEREST-PORTION
*> Apply payment (reduces loan balance)
IF WS-PRINCIPAL-PORTION > WS-CURRENT-BAL
MOVE WS-CURRENT-BAL
TO WS-PRINCIPAL-PORTION
END-IF
INVOKE SUPER "Withdraw"
USING WS-PRINCIPAL-PORTION
RETURNING WS-DEPOSIT-RESULT
ADD WS-INTEREST-PORTION
TO WS-TOTAL-INTEREST
SUBTRACT 1 FROM WS-REMAINING-MONTHS
MOVE WS-INTEREST-PORTION
TO LS-INTEREST-APPLIED
MOVE WS-PRINCIPAL-PORTION
TO LS-PRINCIPAL-APPLIED
MOVE 0 TO LS-RESULT
*> Check if loan is paid off
INVOKE SELF "GetBalance"
RETURNING WS-CURRENT-BAL
IF WS-CURRENT-BAL <= 0.01
SET LOAN-PAID-OFF TO TRUE
END-IF.
END METHOD MakePayment.
METHOD-ID. GetPayoffAmount.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CURRENT-BAL PIC S9(11)V99.
01 WS-ACCRUED-INT PIC S9(9)V99.
LINKAGE SECTION.
01 LS-PAYOFF PIC S9(11)V99.
PROCEDURE DIVISION RETURNING LS-PAYOFF.
INVOKE SELF "GetBalance"
RETURNING WS-CURRENT-BAL
COMPUTE WS-ACCRUED-INT =
WS-CURRENT-BAL * WS-MONTHLY-RATE
COMPUTE LS-PAYOFF =
WS-CURRENT-BAL + WS-ACCRUED-INT.
END METHOD GetPayoffAmount.
METHOD-ID. GetMonthlyPayment.
DATA DIVISION.
LINKAGE SECTION.
01 LS-PAYMENT PIC S9(9)V99.
PROCEDURE DIVISION RETURNING LS-PAYMENT.
MOVE WS-MONTHLY-PAYMENT TO LS-PAYMENT.
END METHOD GetMonthlyPayment.
METHOD-ID. GetRemainingMonths.
DATA DIVISION.
LINKAGE SECTION.
01 LS-MONTHS PIC 9(4).
PROCEDURE DIVISION RETURNING LS-MONTHS.
MOVE WS-REMAINING-MONTHS TO LS-MONTHS.
END METHOD GetRemainingMonths.
END OBJECT.
END CLASS LoanAccount.
Why This Design Works
The LoanAccount class illustrates several important OO design decisions:
Overriding Withdraw to prevent misuse. A loan account should not support withdrawals. By overriding Withdraw to always return error code 7, we ensure that any code treating a LoanAccount as a generic BankAccount (through polymorphism) will get a meaningful error rather than accidentally allowing an impossible operation.
Using SUPER "Withdraw" internally. The MakePayment method calls INVOKE SUPER "Withdraw" to reduce the loan balance. This is a legitimate internal use of the parent's Withdraw method — the LoanAccount class knows what it is doing. The public Withdraw method remains blocked.
Separate InitializeLoan method. Rather than overriding the base Initialize method, LoanAccount adds a new InitializeLoan method. This is because loan initialization requires fundamentally different parameters (principal, rate, term) than a regular account. The base Initialize still works for the common fields (account number, holder name, initial balance).
Business rule encoding. The loan amortization formula, the interest/principal split on each payment, and the paid-off detection are all business rules encoded directly in the class methods. If these rules change (for example, if GlobalBank starts offering interest-only loans), only the LoanAccount class needs modification.
🧪 Try It Yourself: Testing the Hierarchy
If you have access to a Micro Focus COBOL compiler, try these experiments:
-
Create a LoanAccount with a $200,000 principal at 6% for 360 months. Verify that the calculated monthly payment is approximately $1,199.10.
-
Call MakePayment 12 times and observe how the interest portion decreases and the principal portion increases with each payment. This is the fundamental behavior of an amortizing loan.
-
Create an AccountCollection containing one CheckingAccount, one SavingsAccount, and one LoanAccount. Call TotalBalance on the collection. Notice that the loan balance is included as a positive number even though it represents debt — this is a design limitation of our simple hierarchy that a production system would need to address.
-
Try calling Withdraw on a LoanAccount through a BankAccount reference. Verify that you get result code 7, demonstrating that the override works through polymorphic dispatch.
25.17 Error Handling in OO COBOL
Error handling in OO COBOL deserves special attention because errors can occur at multiple levels: within a method, during method invocation, and in the object lifecycle.
Method-Level Errors
Methods should use return codes or output parameters to signal errors, just as in procedural COBOL:
METHOD-ID. ProcessTransaction.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ERROR-OCCURRED PIC X VALUE 'N'.
LINKAGE SECTION.
01 LS-TXN-DATA.
05 LS-TXN-TYPE PIC X(2).
05 LS-TXN-AMOUNT PIC S9(9)V99.
01 LS-RESULT-CODE PIC 9(2).
01 LS-RESULT-MSG PIC X(80).
PROCEDURE DIVISION USING LS-TXN-DATA
RETURNING LS-RESULT-CODE
LS-RESULT-MSG.
*> Validate transaction type
EVALUATE LS-TXN-TYPE
WHEN 'DP'
PERFORM PROCESS-DEPOSIT
WHEN 'WD'
PERFORM PROCESS-WITHDRAWAL
WHEN 'TR'
PERFORM PROCESS-TRANSFER
WHEN OTHER
MOVE 10 TO LS-RESULT-CODE
MOVE "Unknown transaction type"
TO LS-RESULT-MSG
END-EVALUATE.
PROCESS-DEPOSIT.
IF LS-TXN-AMOUNT <= ZEROS
MOVE 11 TO LS-RESULT-CODE
MOVE "Invalid deposit amount"
TO LS-RESULT-MSG
ELSE
INVOKE SELF "Deposit"
USING LS-TXN-AMOUNT
RETURNING LS-RESULT-CODE
IF LS-RESULT-CODE = 0
MOVE "Deposit successful"
TO LS-RESULT-MSG
ELSE
MOVE "Deposit failed"
TO LS-RESULT-MSG
END-IF
END-IF.
...
END METHOD ProcessTransaction.
Invocation Errors
When you INVOKE a method on an object reference that is NULL, the behavior is implementation-dependent — it typically causes an ABEND. Always check for NULL before invoking:
IF WS-ACCT-REF = NULL
DISPLAY "Error: Account reference is null"
MOVE 99 TO WS-RETURN-CODE
ELSE
INVOKE WS-ACCT-REF "GetBalance"
RETURNING WS-BALANCE
END-IF
Defensive Method Design
Maria Chen's rules for OO COBOL methods:
- Validate all inputs at the start of every method. Do not assume the caller provided valid data.
- Return meaningful error codes, not just 0/1. Document every possible return code in the method header comment.
- Never leave the object in an inconsistent state. If a multi-step operation fails partway through, roll back the changes within the method.
- Log unexpected conditions. Methods deep in a class hierarchy may be called from contexts the developer never anticipated. Logging helps diagnose these cases.
25.18 The Student Mainframe Lab: Experimenting with OO COBOL
Using Micro Focus Visual COBOL
If your lab has access to Micro Focus Visual COBOL (Community Edition is available for personal use), you can compile and run all the examples in this chapter. The compilation steps are:
# Compile the base class
cobc -c BankAccount.cbl
# Compile subclasses
cobc -c CheckingAccount.cbl
cobc -c SavingsAccount.cbl
cobc -c LoanAccount.cbl
# Compile the collection class
cobc -c AccountCollection.cbl
# Compile and link the demo program
cobc -x AccountDemo.cbl BankAccount.o CheckingAccount.o \
SavingsAccount.o AccountCollection.o
GnuCOBOL Limitations
If you are using GnuCOBOL, OO features are not fully supported. However, you can still practice the concepts by examining the code and desk-checking it. Alternatively, you can implement the same design in procedural COBOL using the dispatcher pattern from Case Study 25.2.
🧪 Try It Yourself: Building the Complete Hierarchy
Step 1: Start by compiling just the BankAccount class and a simple demo program that creates one account, deposits money, withdraws money, and displays the balance.
Step 2: Add the CheckingAccount class with overdraft support. Verify that the overdraft logic works by attempting withdrawals that exceed the balance but fall within the overdraft limit.
Step 3: Add the SavingsAccount class. Test the Regulation D withdrawal limit by making seven withdrawals and verifying that the seventh is rejected with result code 6.
Step 4: Create the AccountCollection class and the EndOfDayProcess program. Verify that the collection correctly holds different account types through polymorphism and that TotalBalance iterates across all accounts.
Step 5: Add the LoanAccount class. Test the amortization calculation by comparing your calculated monthly payment against an online mortgage calculator. Create a loan for $200,000 at 6.5% for 360 months — the monthly payment should be approximately $1,264.14.
Debugging OO COBOL
Debugging OO COBOL programs has unique challenges:
Object state inspection: Unlike procedural COBOL where you can examine WORKING-STORAGE at any breakpoint, OO COBOL requires examining the instance data of specific objects. In Micro Focus's debugger, you can expand object references to see their contents, but this is less intuitive than viewing flat WORKING-STORAGE.
Method dispatch tracing: When a method is invoked on a base class reference that actually points to a subclass object, the debugger may show the parent class's source code initially before jumping to the subclass's overridden method. This is confusing the first time you encounter it.
Stack depth: OO COBOL programs tend to have deeper call stacks than procedural programs because method calls chain through inheritance hierarchies. SELF calls add to the depth. If your debugger has a stack depth limit, you may need to increase it.
Common mistakes: 1. Forgetting END METHOD or END CLASS — the compiler error messages can be cryptic 2. Missing REPOSITORY entries — the compiler cannot find classes that are not declared 3. Invoking a method that does not exist on the class — if using untyped references, this is only caught at runtime 4. Circular object references — object A references B which references A, preventing garbage collection in some runtimes
25.19 Chapter Review Questions
Before moving to the summary, test your understanding with these conceptual questions:
-
Explain in your own words why the FACTORY section exists separately from the OBJECT section. Why can't factory data and methods simply be instance data and methods?
-
Trace the execution of this code:
cobol INVOKE CheckingAccount "NEW" RETURNING WS-ACCT INVOKE WS-ACCT "Initialize" USING 100 "Test" 500.00 INVOKE WS-ACCT "Withdraw" USING 600.00 RETURNING WS-RESULTWhat value does WS-RESULT contain? (Consider the overdraft limit default of 0.) -
Design question: If you needed to add a "freeze" operation that prevents all transactions on an account (checking, savings, or loan), where in the class hierarchy would you implement it? Would you use a method in the base class, an interface, or something else?
-
Critical thinking: Maria Chen argued against OO COBOL because of limited community support. Derek Washington, coming from a Java background, found OO COBOL intuitive. Whose perspective is more relevant for a production decision, and why?
25.20 Exception Handling and the ON EXCEPTION Clause
OO COBOL provides limited exception handling capabilities through the ON EXCEPTION clause of the INVOKE statement. This is not a full try/catch mechanism like Java's, but it provides a safety net for method invocation failures.
Using ON EXCEPTION with INVOKE
INVOKE myAccount "NonExistentMethod"
ON EXCEPTION
DISPLAY "Method invocation failed"
MOVE 99 TO WS-ERROR-CODE
NOT ON EXCEPTION
DISPLAY "Method invocation succeeded"
END-INVOKE
The ON EXCEPTION clause fires when the method cannot be invoked — typically because: - The method name does not exist on the class - The object reference is NULL (implementation-dependent) - The parameter count or types are incompatible
⚠️ Limitation: ON EXCEPTION catches invocation failures, not logic failures within the method. If the method is found and invoked successfully but throws an error during execution, ON EXCEPTION does not catch it. You still need return codes for method-level error handling.
The Relationship Between Exceptions and Return Codes
In practice, OO COBOL programs use a hybrid approach:
*> ON EXCEPTION handles infrastructure errors
*> (method not found, null reference)
INVOKE myAccount "Deposit"
USING 500.00
RETURNING WS-RESULT-CODE
ON EXCEPTION
DISPLAY "FATAL: Cannot invoke Deposit"
MOVE 99 TO WS-RESULT-CODE
END-INVOKE
*> Return code handles business logic errors
*> (invalid amount, frozen account)
EVALUATE WS-RESULT-CODE
WHEN 0
DISPLAY "Deposit successful"
WHEN 1
DISPLAY "Invalid amount"
WHEN 2
DISPLAY "Account frozen"
WHEN 99
DISPLAY "System error"
END-EVALUATE
This belt-and-suspenders approach ensures that both infrastructure and business errors are handled, preventing the program from silently continuing with bad data.
25.21 OO COBOL in the Broader Technology Landscape
To close this chapter, let us place OO COBOL in the context of the broader technology landscape that COBOL developers face.
Where OO COBOL Fits on the Modernization Spectrum
Pure Procedural OO Thinking in OO COBOL Java/C#
COBOL Procedural COBOL Syntax Rewrite
←────────────────────────────────────────────────────────────→
Lower risk Moderate risk Higher risk Highest risk
Lower benefit Good benefit High benefit Highest benefit
Proven approach Pragmatic Limited adoption Industry standard
Most organizations find their sweet spot in the "OO Thinking in Procedural COBOL" zone — applying design principles without requiring the OO syntax. A smaller number use OO COBOL syntax (primarily Micro Focus shops), and many eventually migrate selected components to Java or C#.
The Compound Effect of Good Design
Regardless of whether you write CLASS-ID. BankAccount. or structure your procedural programs to act like classes, the design principles from this chapter pay compound dividends:
- Encapsulation reduces the blast radius of changes. Modifying interest calculation logic in one class or program does not affect unrelated code.
- Inheritance (or its procedural equivalent, shared copybooks and consistent CALL interfaces) eliminates code duplication.
- Polymorphism (or EVALUATE-based dispatch) makes the system extensible without modifying existing code.
- Interface contracts (whether formal INTERFACE-ID or documented copybook layouts) enable parallel development by multiple teams.
These benefits accrue regardless of syntax. OO COBOL makes them explicit in the language; procedural COBOL requires discipline to achieve them.
25.22 Summary
Object-Oriented COBOL represents one of the most ambitious extensions to a programming language that was originally designed for sequential batch processing. The COBOL 2002 standard introduced a complete object model: classes (CLASS-ID), methods (METHOD-ID), inheritance (INHERITS), interfaces (INTERFACE-ID), object references (USAGE OBJECT REFERENCE), and factory patterns (FACTORY section).
The syntax is verbose but consistent with COBOL's philosophy of explicit, self-documenting code. The INVOKE statement replaces the dot operator of Java, SELF replaces this, and SUPER enables calling parent class methods from overriding methods.
Yet the reality of OO COBOL adoption is more complex than the syntax. Limited compiler support, a procedural workforce, competition from Java and C#, and the sheer weight of existing procedural codebases have all constrained adoption. The most pragmatic approach may be James Okafor's "OO thinking in procedural code" — applying design principles like encapsulation and single responsibility without requiring the OO syntax.
Understanding OO COBOL matters regardless of whether you use it directly. It demonstrates that COBOL is not a frozen language, it illuminates design principles that improve procedural code, and it prepares you for the modernization decisions that every COBOL shop eventually faces.
🔵 Legacy != Obsolete: OO COBOL's existence — even with limited adoption — disproves the myth that COBOL cannot evolve. The language committee, compiler vendors, and forward-thinking shops have pushed COBOL's boundaries for decades. Whether OO COBOL represents the future of COBOL or a historical curiosity, the principles it embodies are timeless.
🔗 Looking Ahead: In Chapter 26, we will explore inter-language communication — how COBOL programs call C, Java, and assembler routines. Where OO COBOL tries to bring object-oriented design inside COBOL, inter-language communication lets COBOL collaborate with other languages, each contributing its strengths. Both approaches sit on the modernization spectrum; both have their place.