Volume 3, Number 1, February 1995, pages 13-19

An M Implementation of
Object-Oriented Programming,

by Don Gall

The Blind Men and the Elephant
by John Godfrey Saxe


It was six men of Hindostan, to learning much inclined,
Who went to see the elephant, (though all of them were blind)
That each by observation might satisfy his mind.
... it seems the elephant is very like a wall.
... this wonder of an elephant is very like a spear.
... the elephant is very like a snake.
... the elephant is very like a tree.
... this marvel of an elephant is very like a fan.
... the elephant is very like a rope.

And so these men of Hindostan disputed loud and strong,
Each in his own opinion exceeding stiff and strong,
Though each was partly in the right and all were in the wrong.

Introduction

The literature of object-oriented programming has brought with it a few new terms, the redefinition of a few old terms, and a few new concepts. Much of what is presented under this broad umbrella of object-orientation is simply a more precise formulation of older concepts and goals that good programmers have dealt with for at least three decades. As an example, most of us moved to modular (or re-usable) programs after realizing that it was not efficient to maintain thirty or forty programs, all with similar or identical sections of logic. In addition, we quickly learned that a large programming project could be completed more efficiently if it were first broken down into smaller logical elements with predefined interactions. It is not surprising that these two examples are parts of object-oriented programming.

The terminology of object-orientation includes the definitions of words such as object, class, instantiation, abstraction, encapsulation, hierarchy, modularity, re-usability, typing, concurrency, inheritance, and persistence. This paper does not provide a discourse on object-oriented programming. A number of authors – notably Booch, Meyer, Cox, and Khoshafian – already have produced extensive literature on the subject. [1, 2, 3, 4] Similarly, I will not attempt to inject opinions as to the relative importance of these object-oriented concepts to "true object-oriented programming". There is sufficient disagreement among the four texts cited for the reader to realize that there is not one generally accepted definition of object-oriented programming. Object-oriented programming has lived up to the twelve-year-old prediction of Rentsch: "Everyone will be in favor of it. Every manufacturer will promote his product as supporting it. Every manager will pay lip service to it. Every programmer will practice it (differently). And no one will know just what it is." [5]

Object-oriented programming has become the modern version of the tale of the six blind men and the elephant. This article presents yet another blind man's view of the "elephant." This particular blind man brings with him the prejudices of thirty-nine years of dealing with real-world programming problems, with more than half of those years spent in the M programming language environment.

The Evolution Toward Objects

About sixteen years ago, my firm began its movement toward object-oriented programming. This was done not because of awareness of the field, but rather as an attempt to eliminate many tedious tasks of programming. Early efforts went into developing programmer tools to do those things which we, as programmers, least liked to do.

The first of these tools was a questionnaire driver, which allowed us to quickly structure a series of questions for user input and error-checking. This questionnaire driver was later expanded to provide for screen-formatted entry and editing, and to generate review screens and a data dictionary as side benefits. Most developers of software tools during this period pursued a methodology for creating a program or programs for each set of questions that formed a user input screen. We elected to use some of the unique attributes of M to develop generic programs, which used a specially formatted set of data to provide the entry and edit functions for any question set. This move toward "parameter-driven software" has turned out to be a fortuitous choice. Our subsequent development of a single-menu program and a number of report writers followed this parameterized approach. The development effort described here follows this same path.

Four years ago, we began to explore how the extensive and sometimes conflicting information in the object-oriented literature could be used to improve our software and software-development methodology. All of the hype about object-oriented programming has caused us to search for a magic bullet. What we found was a collection of very special BBs. In retrospect, this should not have been unexpected. Database-management systems deal with the mathematical- and computer-modelling of information. Those of you who have spent any time using computers to solve partial differential equations will recognize that the methods used to solve elliptic partial differential equations are not applicable to the solution of hyperbolic differential equations. Why then should we expect to find one grandiose methodology applicable to the incredibly broad array of problems worked on by computer programmers?

We gave up on finding a magic bullet and focused our attention on examining the very special BBs to determine which of these would help us reach our objectives. In order to proceed, we first had to establish the objective we were hoping to reach. In broad terms, these objectives were to:

Our first efforts were directed at the deficiencies of the existing software. The three major deficiencies may be summarized as:

With the exception of the questionnaire driver, a generalized table-maintenance structure and a generalized menu driver, the existing software made little use of generalized procedures that could be inherited by multiple, similar applications. Although M allows the passing of parameters to extrinsic functions, this functionality had not been properly exploited in the existing software.

The most glaring weakness in our software was a complete lack of encapsulation. Encapsulation requires that only those methods (programs) within a class have knowledge of the structure of the data within the class. The structure of our many globals was embedded in the logic of almost every program. As one bad example, the modification of the data dictionary for our General Ledger would have required changing at least seventy-five other programs. It is not surprising that the structure of the General Ledger was not changed in seventeen years.

The concept of encapsulation is antithetical to M because M makes it so easy for a programmer to access global data. Given the current philosophy of M, the only way to enforce encapsulation is by decree. It is a decree worth issuing. We quickly placed encapsulation high on our list of object-oriented programming requirements. For the class of problems that we deal with, we believe that encapsulation is an absolute necessity and will later describe how we approached this issue.

In addition to encapsulation, we added three other elements of object-oriented programming that had not been present in our previous software. These were a formal declaration of classes, the introduction of data types, and the formalization of inheritance.

Implementation

Before proceeding on to the implementation of these changes, let me define some classes that will be used as examples. First, it is important to note that the definition of a particular class of objects is somewhat arbitrary. For example, does the Vendor Class contain vendor demographic information only or does it also contain unpaid and paid invoice history? The best solution may well vary depending upon the available tools and what functionality you need with your software. For this article, I will define the following classes of objects:

Formal Definition of a Class

Our formal definition of a class includes the class ID, the description of the class, a definition of the attributes of the class and a data dictionary for the class. This information is stored in a class definition global (^UCL) by class ID (CLSID) along with other information about the class, which will be defined subsequently. The attributes of a class (often referred to as instance variables) are primarily the indexes and fields needed to specify the state of the object and thus also the data dictionary. Each attribute definition includes:

The ability to specify the Primary Class ID as a super class of another class has been very powerful and will be described in more detail later.

M has great flexibility because it has no data-typing of variables. Data-typing of attributes was added to improve the programmer efficiency and to enhance the capabilities of the general report writer. The most commonly used data types are:

$ Dollars (stored as pennies, displayed as dollars)
D4 4-digit dates (stored as YRMO, displayed as MO-YR)
D6 6-digit dates (stored as YRMODA, displayed as MO-DA-YR)
ISF Index sequential variable (0 level = counter)
NI Numeric Integer format
NF Numeric Floating point format
ST String Text Format
STn n lines of String Text
STI Infinite lines of String Text

The data types are stored in a programmer-accessible table structure. Display algorithms and other programmer-supplied information are used to prompt the programmer with information during attribute and questionnaire definition. The general report writer uses this information to construct reports and to display report quantities.

The data dictionary entries are defined in typical M global format in terms of the previously defined attributes and stored as strings in the class-definition global ^UCL(CLSID) as:

"^XYX(attr_i, attr_j, ...) = attr_n~attr_m~..."

where attr_i, attr_j, attr_n, etc. are previously defined attributes for this class. This format allows us to maintain our efficient hierarchical structure while simultaneously presenting a relational view to external SQL applications and to our internal report writer.

Programmers use the programs to create the structure; screen format(s) of user screens use the attribute definition, the data types, and the data dictionary to assist in the construction and display format for each user screen.

Next, look at the operations we need to perform on a class. These operations have been categorized in many ways by many authors. Our particular list is as follows:

Create, Delete, and Modify Operations

In our particular implementation, the Create, Delete, and Modify operations for the classes Vendor Demographics and General Ledger Chart of Accounts are controlled by and encapsulated within those respective classes. No other classes are allowed to Create, Delete, or Modify the state of these classes.

On the other hand, the creation of an instance of the Vendor Unpaid Invoice Class is initiated by the Vendor Invoice Journal Class and therefore created by a communication from that class. The same transaction also communicates a Modify message to the General Ledger Totals Class. In a similar manner, the Cash Disbursements Journal Class initiates the deletion of an instance of the Unpaid Vendor Invoice Class and the creation of an instance of the Vendor Paid Invoice Class while also modifying the General Ledger Totals Class.

The object-oriented programming literature frequently describes communication between classes in terms of the sending and receiving of messages. Unfortunately, M is not (at least at the present) well suited to the rapid communication of messages between separate processes. Therefore, instead of sending messages between classes, we have elected to send messengers in the form of extrinsic functions. These extrinsic functions take the form:

   S X=$$^GLMOD(parameter_list)

This particular example is a Modify operations created by the General Ledger Totals Class to enable other classes to change the state of an instance of the General Ledger Totals Class. Another class such as the Cash Disbursements Class may change only the amount of money in any General Ledger Account by invoking this extrinsic function with a suitable parameter list. The value returned signifies whether the General Ledger Class was able to complete this task.

Locate Operation

Classes such as the General Ledger Chart of Accounts must create a specific Locate extrinsic to enable other classes such as the Vendor Invoice Class to identify an instance of the class given either a General Ledger Account Number or an Account Description. The Locate extrinsic enables a user who is entering a vendor invoice to locate and choose the appropriate General Ledger Account to be debited in a particular transaction. A second Locate extrinsic created by the Vendor Demographics Class enables the user to select the appropriate Vendor ID.

The General Ledger Locate extrinsic is:

   S X=$$^GLF()

The extrinsic returns the user-determined General Ledger Account Number.

Inquire Operation

Because of the structure of the formalized class definition, it has been possible to construct an Inquiry function, which allows any class to inquire of any other related class. For example, a specific instance of the Vendor Invoice Journal Class contains the value of the Vendor ID and therefore can obtain the value of any attribute from the Vendor Demographics Class for that specific Vendor ID. This general Inquiry function is an extrinsic function defined by

S X=$$^UINQ(CLSID,def_attr,attr_value.ret_attr
where
CLSID is the Class ID of the class containing the information
def_attr one or more attribute IDs of CLSID needed to identify the specific instance
attr_value the instance value(s) of def_attr
ret_attr the attribute ID or IDs of CLSID the invoking class wished to have returned in the variable x

Report Operation

With the knowledge of the data dictionary and the class attributes, a general purpose report writer was much simpler to write. This report writer draws upon the ability of an attribute definition to specify an equality relationship of an attribute from a super class. Thus, all attributes from super classes used by the primary class are available to the report writer. For example, a report for the Vendor Invoice Journal Class can access the vendor name and address from the Vendor Demographics Class because it has knowledge of the vendor ID, and the Vendor ID attribute in the Vendor Invoice Journal Class contains the information that it is related to the Vendor ID attribute in the Vendor Demographics Class. In a similar manner, the Vendor Invoice Journal Class can access the description from the General Ledger Chart of Accounts Class with the knowledge of the General Ledger Account Number and its known link to the General Ledger Chart of Accounts Class.

Reports may be created by invoking

   D ^UPA(CLSID)

which allows the user to create and store any number of reports for the specific class. Any of the previously created reports are executed and printed by invoking

   D ^UPP(CLSID)

Formalizing Inheritance

As we developed a process for formally defining a class and began to examine how to best provide for these six general operations, we realized that the data structures associated with these classes also could be grouped into classes. These classes of classes, or metaclasses, differ from the classes previously defined in that the instance of a metaclass is not an object but rather another class. [6]

As an example of the use of metaclasses, we can state that both the Vendor Demographics Class and the General Ledger Chart of Accounts Class have a single variable as an index in our data structure:

   ^VENDOR(ID) = data fields
   ^GLCHART(ACCT) = data fields.

These and many other examples within our software such as Client Demographics, Attorney Table, and Type of Law Table can be grouped into a metaclass we refer to as the Single Index Metaclass (SIM). The definition of this Single Index Metaclass is actually broader than might be inferred from the two examples. It can also include examples in which additional constant (literal) indexes are defined such as:

   ^VENDOR(ID,"NAME) = vendor name
   ^VENDOR(ID,"ADDR") = vendor address fields
   ^VENDOR(ID,"PHONE") = vendor phone number fields.

The Vendor Invoice Journal, Vendor Unpaid Invoice, and Vendor Paid Invoice Classes are instances of a metaclass we have termed the Double Index Sequential Metaclass (DISM). This structure is a many-to-many-to-one structure. The Vendor Invoice Journal can have any number of accounts and amounts to be debited for each invoice entered, and there can be any number of invoices for any given date. In M global structure, this is represented as:

   ^VENDINV(DATE,I) = specific invoice fields
   ^VENDINV(DATE,I,J) = specific account and amount fields.

This general structure is also present in the Cash Disbursements Journal, the Cash Receipts Journal, and in a number of other areas of our software.

Thus far, we have found it useful to define only these two metaclasses. A possibly more appropriate point to make is why we found it useful to define any metaclasses. To understand this, consider the six operations we defined (Create, Delete, Locate, Inquire, Modify, Report). Creating a metaclass enables us to write a generalized M routine for an operation that then applies to all classes that are instances of that metaclass. This is accomplished by structuring the parameters contained in the formal class definition file.

Both the SIM and DISM Create routines (and the few additional classes which have programmed Create routines) employ a set of programmer-defined questionnaires based upon information from the class attributes definitions. A single extrinsic function paints the input screen, executes all questions, and returns the answer as a string to the SIM or DISM (or programmed) Create routine.

Generic SIM and DISM Delete and Locate routines have been developed.

Summary

We now view our software development on three levels:

None of the ability to execute "normal" M programs is lost. But the tedium of developing one more enter, edit, review, or print program has all but disappeared.

The other major impact this development effort has produced is that we can now realistically consider developing "plug-compatible" components. The components of our previous law-firm package were so intertwined that is was difficult to modify one section without rewriting major parts of the entire system.

With the newer structure, we have developed a General Ledger Class that only communicates by extrinsic functions with other classes. The Accounts Payable module is composed of a number of classes and communicates with the General Ledger and the existing Accounts Receivable through extrinsics. In this manner, we are able to modify the various components of the system one at a time, changing only the interacting portions as we go.

In all fairness, it must be pointed out that the transition to object-oriented programming has been neither painless nor pure. Object-oriented programming requires programmers to approach issues from a different point of view. There must be a continuing education and reinforcement of the principles of the methodology. The tendency of M programmers to violate encapsulation is a good example of this. Second, our implementation is not pure! In a few instances, such as the application of cash receipts, we have made the conscious decision to violate encapsulation in order to improve performance.

The results thus far have been a significant decrease in the number of programs needed, in the complexity of the programs we now write, and in the time required to develop an application. There has been a significant increase in the re-usability of our programs. There has been a minimal change in the fundamental structure of our existing data. And, programmers spend more time thinking and less time typing.


Don Gall, Sc.D., is president of Omega Legal Systems, Suite 406, 7272 E. Indian School Road, Scottsdale, AZ 85251; phone 602-946-6772; fax 602-947-1427. He wrote for this publication on comparing COBOL and Relational Database Software in November 1992.


Endnotes

[1] G. Booch, Object Oriented Design with Applications (Redwood City, CA: Benjamin/Cummings, 1991).

[2] B. Meyer, Object Oriented Software Construction (New York, NY: Prentice Hall, 1988).

[3] B. Cox, Object-Oriented Programming: An Evolutionary Approach (Reading, MA: Addison-Wesley Press, 1986).

[4] S. Khoshafian and R. Abnous, Object Orientation: Concepts, Languages, Databases, User Interfaces (New York, NY: John Wiley & Sons, Inc., 1990).

[5] T. Rentsch, "Object-Oriented Programming," SIGPLAN Notices 17:12 (September 1982): 51.

[6] G. Booch, 1991, p.119.