PROGRAMMING PARADIGMS

Babbit's Guide to OOP

Michael Swaine

"He was earnest about these objects. They were of eternal importance, like baseball or the Republican Party."

--Babbit, Sinclair Lewis

We appear to be witnessing the mainstreaming of object-oriented programming. Regardless of the actual number of lines of code written this year or the next in object-oriented languages as compared with other languages, it looks as though object-oriented programming is on the verge of becoming the dominant programming paradigm in personal computer software development. An area that had recently been a frontier has now nearly been cleared for farming, 4-H, and the Rotary Club.

Although DDJ has been investigating OOP techniques and the implications of the paradigm for some time now, you'll be seeing more detail in the near future as we cover this phenomenon. In April, Mike Floyd's article set the stage on getting started in object-oriented programming. In the coming months, Kent and Jeff will be analyzing significant new developments in OOPs. This month's "Programming Paradigms" is a sort of bridge between Mike's piece and greater OOP coverage coming in DDJ in that it defines some basic terms and points out some of the chief issues in OOP without attempting to draw any deep conclusions.

Tell Me Again, What Is Object-Oriented Programming?

Object-oriented programming (or software design) is "the construction of software systems as structured collections of abstract data-type implementations," according to Bertrand Meyer. It is a programming paradigm in which the emphasis is on the thing to be manipulated rather than on the functions to be performed. The things to be manipulated are represented as abstract data-type representations, and these representations are called classes, that's why Mike Floyd said in April that OOP could be called Class-Oriented Programming.

Defining "object" to mean "an instance of an abstract data-type implementation" is a little dry; especially because the idea behind the concept of an object is much more intuitive than this. Object-oriented programming began with the language Simula, which, while capable of much more, was conceived as a language for designing simulations. If you are writing a simulation in an object-oriented language, the objects of your simulation will be exactly the software simulations of the physical objects in the real-world system you are simulating. A valve, a gear, and an engine are all good candidates for objects in some systems. Most programs are not direct simulations of real-world systems, but the simulation view of an object is still useful to keep in mind when evaluating candidates for objecthood. A screen, a grafPort, and a window are some less real objects.

Brad Cox defines objects thus: "An object is some private data and a set of operations that can access that data. An object is requested to perform one of its operations by sending it a message telling the object what to do." This sounds like applying a function to some data, hardly a revolutionary concept in programming. The difference is all in how the operation gets matched to the data on which it operates. Because an object is not a data structure or a set of procedures but a combination of the two, a powerful new approach to matching the operation to the data is possible. Messages are the key.

A message is a function call with a specified receiver. Sending the message triggers a selection mechanism that uses the receiver name to branch to the chunk of code appropriate to that receiver. The technique is simple, but the effect is dramatic in terms of the division of labor in software development. When a programmer writes code that causes a message to be sent to an object, the responsibility for ensuring that the operation is appropriate to the data rests not with the programmer but with the person who wrote the definition of the class to which the object belongs.

The result is reusable software components, at least in theory. Everyone who writes about object-oriented programming seems to agree that the motivation for using the technique is something like "increased productivity, maintainability, and reliability through reusable software components" that can be plugged into new systems. These reusable components in OOP are classes. Classes in a true object-oriented system are not independent data types, but are part of one large structure with the structuring principle being inheritance.

When a new class is created, it is defined to be a subclass of some existing class and inherits all the functionality of the parent class. What inheritance buys you is the ability to define key elements of the functionality of a system once, to use these elements as building blocks, and to do it all smoothly and naturally. The smoothness of the process is the real advance that OOP represents; as Zack Urlocker has said, "Many programmers are pleasantly surprised to find that object-oriented languages encourage them to use techniques that they have been faking for years in other languages."

Issues in Object-Oriented Programming

The preceding topics of objects, classes, messages, and inheritance need to be discussed at the beginning of any overview of OOP because they are fundamental to an understanding of more advanced topics. The order of presentation of these other topics, however, may depend on one's particular viewpoint on OOP. I have deliberately avoided presenting them in any of the logical sequences in which they could be placed and have instead treated each issue as an item unto itself, a sort of reusable editorial component. The order is alphabetical, and the presentation is encyclopedic in structure (though not in scope).

Abstract data types -- To specify an abstract data type is to define a set of data structures strictly in terms of the features of the structures and the operations defined on them, and not in terms of the actual physical implementation of the data structure. Classes in object-oriented programming are abstract data types.

Client relationship -- In addition to the inheritance relationship between classes, one class may be a client of another. This simply means that the implementation of the first (client) class relies on the second (supplier) class, for example, by using objects belonging to the supplier class. Deciding which relationship should apply is sometimes tricky but more often obvious. The class HOUSE would naturally inherit from the class BUILDING, but could be a client of classes ROOF and WALLS.

Concurrency -- One potential benefit of object-oriented programming is concurrency. Although current object-oriented systems do not provide for concurrency, the decision to build a system from independent objects that communicate via messages naturally suggests an implementation that uses concurrent processors.

Dynamic binding -- Binding or linking is the process of putting together elements of functionality from different sources into one executable image. Dynamic (or delayed, or late) binding is binding after compile time, probably during execution. Dynamic binding in object-oriented systems assigns to the supplier, rather than to the client, the responsibility for ensuring that an operation is appropriate to the data on which it operates.

Efficiency -- The clearest benefits of object-oriented programming are in the design of large and complex systems. When it comes to the small details of implementation that strongly affect system performance, it seems to be widely acknowledged that calling a function is usually faster than passing a message. Current personal computer hardware could, with some justification, be referred to as C machines; hardware ideally suited to object-oriented programming has yet to be brought successfully to market and may have to wait for parallel architectures. Language and operating system support for object-oriented programming is on the increase and may be more important than hardware support, however, as operating systems impose themselves more and more between the programmer and the hardware.

Encapsulation -- To the user of an object the data is invisible, the procedures for accessing and operating on that data is visible. This is encapsulation, and Meyer has shown how it can be implemented in even a non-object-oriented language such as Fortran.

Hybrid paradigms -- Smalltalk is the best-known example of a pure object-oriented programming system in which all actions are implemented as messages sent to objects. Objective C and C++ are well-known hybrids that add object-oriented features to C but retain some non-OOP features. The arguments usually advanced for the pure approach are just the arguments for object-oriented programming: reliability, maintainability, and ease of development. The usual arguments for the hybrids are familiarity and speed.

Information hiding -- Information hiding is a desideratum of modular programming: All information is a module should be private to that module unless made public through a definition known as the interface. Encapsulation is one answer to what should remain private and what should go into the interface.

Modularity -- Meyer states five criteria of modularity: decomposability, composability, understandability, continuity, and protection. Modules should aid in the top-down analysis of a problem into subproblems, permit building large systems for smaller building blocks, be understandable on their own, and be sufficiently separate from one another that a design change or run-time abnormality affecting one module will not significantly affect the whole system. The desire for modularity is a primary motivation behind the development and use of object-oriented programming.

Multiple inheritance -- Eiffel has it; most other OOPs don't. Multiple inheritance is the capability for a class to inherit features from two or more ancestor classes. With only single inheritance, the inheritance structure is a tree; with multiple inheritance, it's a forest. Multiple inheritance raises some hotly-debated questions, such as how to resolve clashes between features of the parent classes. Some cases of multiple inheritance are what Meyer calls marriages of convenience in which the two ancestors complement each other nicely. On the other hand, it may be convenient to create a class that inherits from two parent classes that are not entirely complementary: Each parent may contribute a version of the same method, for example.

Polymorphism -- This is the ability to send the same message to different objects and have them respond differently.

Renaming -- This is a mechanism that permits references to do the same thing under different names, depending on the class involved. It is used in Eiffel to resolve clashes in multiple inheritance.

Redefinition -- This is the mechanism that allows the client programmer to use the same name to refer to different things, depending on the class to which it is applied. It makes polymorphism possible.

Repeated inheritance -- A special case of multiple inheritance is repeated inheritance in which one class P is an ancestor of another class C in more than one way, possibly a direct ancestor. It presents difficulties beyond those of ordinary multiple inheritance.

Reusability -- The issue was stated well by Meyer: "Why isn't software more like hardware? Why must every new development start from scratch? There should be catalogs of software modules, as there are catalogs of VLSI devices: When we build a new system, we should be ordering components from these catalogs and combining them, rather than reinventing the wheel every time. We would write less software and perhaps do a better job at that which we do develop. Then wouldn't the problems everyone laments the high costs, the overruns, the lack of reliability just go away? Why isn't it so?"

Selective inheritance -- While multiple inheritance is an issue in OOP, selective inheritance, the ability to exclude features in inheriting from a class is not. No object-oriented language implementation seems to support it, probably because it is a Pandora's box. But selective inheritance would allow object-oriented systems to more closely match the world-model that we carry in our heads. We have no trouble thinking of an ostrich as a flightless bird, but in the object-oriented aviary this is a hard thought to think. What we can do is use redefinition to make flight mean something different for this particular bird. Depending on how you define the problem (or whether you think there is a problem) this may or may not be a solution.

References

Brad Cox. Object-Oriented Programming: An Evolutionary Approach. Addison-Wesley, 1986-7.

Bertrand Meyer. Object-Oriented Software Construction. Prentice Hall, 1988.

Bertrand Meyer. "Reusability: The Case for Object-Oriented Design." IEEE Software, March 1987.

"Whitewater's Actor: An Introduction to Object-Oriented Programming Concepts," Zack Urlocker, Microsoft Systems Journal, March 1989.

"A Class Act," Michael Floyd, DDJ, April 1989.


Copyright © 1989, Dr. Dobb's Journal