Mike is a technical editor for DDJ and can be reached through CompuServe at 76703,4057 or through MCI: MFLOYD.
An object in the real world is something with mass, an associated weight, and volume; it has other attributes that further refine its description and, in some cases, there may be some procedure or action that describes its function. Our world is filled with objects, so it seems only natural to describe and solve problems in terms of objects as well.
This idea is the basis for object-oriented programming (OOP). As you'll soon see, however, a programming language requires more than just objects to be called object-oriented.
This article begins with a tour of the concepts in object-oriented programming. I'll cover the basic concepts including objects, classes, inheritance, and polymorphism. Next, I'll discuss how these concepts are brought together to do what is called "object-oriented programming." In addition, I'll examine some of the reasons you might consider OOP. Finally, I'll cover some of the popular object-oriented languages such as SmallTalk and Actor, as well as some of the traditional languages, like C (C++), that have object-oriented extensions.
As in the real world, an object is something with a set of attributes and features that describe its nature and functionality. Objects are represented in most OOP languages as data structures. The degree of success that an object can be described depends, in part, on how well those data structures can be implemented. This is a key point when considering the various OOP languages.
At the program level, objects can be thought of as Pascal records or C structures. Just as a record can contain other records, an object can contain other objects. Messages are used to communicate with objects. When a message is sent to an object, the object uses a set of procedures (called methods) to respond to that message. Messages are analogous to function calls in other languages. As an example, consider the following statement:
3 factorial
In this example, 3 is an object to which the message factorial is sent. The extent to which messages are supported depends on the implementation, as you'll soon see.
In concept, objects belong to categories or classes of objects. These classes can in turn be subclasses of broader classes. Looking at it another way, an object is an instance of a particular class. Classes, then, provide a way to categorize a given set of objects.
As an example, consider Figure 1, which describes a class of objects known as "aircraft." Within the aircraft class are subclasses of objects corresponding to particular types of aircraft. For instance, "private plane" is a subclass of the "aircraft" class. Continuing down the graph you'll note that ""Cessna 182" is a subclass of "private plane" and that "Flying Lady" is an object that describes a particular Cessna 182.
Note that each subclass is connected to its "parent" class by a link. Since a private plane is an aircraft, the link between these two is called an IS-A link. It is this network of links (called a semantic network) that allows us to deduce that the Flying Lady is also an aircraft.
A class definition is used to describe a new class of objects. This is analogous to type definitions in other languages. Defining a new class of objects is referred to as data abstraction. Objects are therefore described as implementations of abstract data types.
As I mentioned earlier, OOP treats data structures as primary. At this level, we are talking about classes of objects. Indeed, OOP may well have been called COP (class oriented programming) since it is better to think in terms of classes of objects rather than individual objects (instances).
Just as messages are specific to objects, methods are specific to classes. A method describes how a particular class of objects will behave. Methods are analogous to program code in other languages.
The difference between objects and classes is often confused. Keep in mind that objects are created at run time and are the instances of a class, while classes are a static description of an object set that exists in the program.
Another concept that confuses traditional programmers is the notion that, at least in "pure" OOP, a module and a type are the same. We can therefore state the following identity:
module = type
What this implies is that the class description includes both the defining attributes of an object and the services it provides. Indeed, this identity implies the dual nature of classes.
Inheritance is a mechanism that allows an object to inherit properties from a class of objects. For instance, consider Figure 2, which adds some descriptions to the various classes from Figure 1. The description for aircraft, for instance, states that all aircraft have landing gear. From this, you can deduce that the Flying Lady must have landing gear since it is a Cessna 182 and a Cessna 182 is an aircraft. In fact, the description of "Flying Lady" refines the notion by stating that this particular aircraft has fixed landing gear. As presented, this is known as single inheritance since an object inherits its attributes from a single parentclass (or ancestor).
There are, of course, extensions to the basic inheritance concept. Multiple inheritance, for instance, allows you to declare a class as heir to more than one parent class. Repeated inheritance, on the other hand, allows you to declare a class as heir to more than once to the same class.
Polymorphism allows program entities to refer to objects in more than one class, and through dynamic binding the ability for those objects to respond uniquely to the same message. This is an important feature since it allows us to group dissimilar classes (analogous to an array of mixed types).
So, what is OOP and what makes a language object-oriented? You might say that object-oriented programming is the combination of data abstraction, inheritance, and polymorphism. OOP treats data as primary, therefore, object-oriented programming is a style that modularizes a program on the basis of its data structures.
An object-oriented language, then, must support abstract data typing for the creation of new classes, some degree of inheritance so that new objects created at run time can take on the properties of predefined classes, and polymorphism so that a single message can elicit different responses from different object classes.
A true OOP language should also support garbage collection. The development system should automatically deallocate memory for objects that will no longer be used in the system. This feature exists in most object oriented languages, but not in many of the extended languages such as C++.
A colleague of mine once noted that, "if an object is comparable to a C structure, why do I need another paradigm?" Supporters of object-oriented programming point out that typical top-down approaches to software development lead to narrowly defined procedures that are specific to a given problem. Such procedures must be changed as the problem at hand changes. OOP on the other hand, promotes a modular approach to programming. Because objects stand on their own as individual components, they can be readily extended to handle new features. In addition, objects can be reused, either by other parts of the system, or in other applications.
Inheritance in particular promotes reusability because modules (classes) are extensions of existing modules (parent classes). Polymorphism, on the other hand, promotes extendability and flexibility of the system.
Besides reusability and extendibility, there are other programming issues that are solved by object-oriented programming. For instance, earlier I mentioned that objects, just as a Pascal records or C structures, can contain other objects. But there are some characteristics of objects that are beyond the scope of procedural languages. For instance, objects must be able to "share" objects with other objects (comparable to sharing a field of a record with another record). This is not possible with Pascal.
Another problem difficult to solve in procedural languages is the ability to create new objects on the fly (comparable to creating structures dynamically at run time). Indeed, this is one of the features that allows the creation of more "intelligent" applications. This allows the system to change its behavior without programmer intervention, and has important consequences in data base and knowledge-base applications.
As you might guess, there are a number of object-oriented programming languages with disparate philosophies on how these features should be handled. Just as a hammer is better than a screwdriver at pounding a nail, each of the object-oriented languages caters to particular needs. By surveying some of the popular object-oriented languages, you may be able to come to grips with which language is most suitable for your purposes:
SmallTalk --Implemented as an interpreted environment, SmallTalk is a typeless language that favors dynamic binding; because no type checking is performed, binding of structures takes place at run time. SmallTalk was developed at Xerox by Alan Kay and his associates in the mid 1970s. SmallTalk has since then been implemented for a number of platforms and Xerox has formed a subsidiary called ParcPlace Systems to fully support the SmallTalk environment.
Everything in SmallTalk is an object including the environment that SmallTalk runs in. Therefore, SmallTalk does not distinguish between objects and classes. SmallTalk can be described as a tree of classes where object is the root class, and the leaves of the tree are considered subclasses. In this scenario, a subclass is an instance of a higher-level class called a metaclass.
SmallTalk supports most of the concepts developed in this article including data abstraction, inheritance, polymorphism, and garbage collection. Actor --Actor, which is implemented as an interpreter, still uses a more procedural approach with control and block structures. The Actor language set supports messages and methods, is strongly typed, and includes a built-in library of classes similar to SmallTalk.
Actor, like SmallTalk, does not distinguish between objects and classes in the sense that object is the "ultimate" class that all other class will be built from. Actor supports all the "pure" object-oriented concepts described in this article including data abstraction, message sending, single inheritance, and polymorphism.
Eiffel --Eiffel is a compiler that provides a complete object oriented programming environment. Eiffel supports most of the concepts described in this article including multiple inheritance, dynamic binding, and garbage collection. Eiffel provides a complete library of traditional tools as classes. These classes include windowing and graphics based on X Windows, lists, hash tables, binary trees, and more.
An interesting feature of Eiffel is that it generates C as intermediate code. This allows for portability as well as opening the door to cross-development in the C environment.
C++ --C++, developed by Bjarne Stroustrup at AT&T, offers a number of extensions to the C language. C++ by nature distinguishes objects from classes. As with C structures, classes are declared and defined separately. C++ supports the notion of "friend" routines that make it possible to call C++ routines from C. C++ also supports operator overloading, which allows you to use the same name for different operations.
C++ handles the issue of dynamic binding by forcing the programmer to declare classes as virtual. Defaulting to static binding means that the compiler can implement calls efficiently. This, of course, has an effect on the polymorphic nature of the declared class.
Current implementations of C++ only support single inheritance. A C++ standard is expected from AT&T sometime this year. Some of the issues still to be addressed include multiple inheritance.
Of course, all memory management and garbage collection is left to the programmer. In keeping with traditional C, the C++ programmer merely defines Create and Destroy procedures.
Objective-C --Objective-C departs from C++ in the sense that the object-oriented portion of the language is untyped. Objective-C provides a library of classes that roughly parallel the classes built into SmallTalk. Providing untyped classes allows Objective-C to emphasize dynamic binding and polymorphism, although it only supports single inheritance. Again, allocation and deallocation of memory is in the hands of the programmer.
Lisp --Because of the interest in the artificial intelligence community, several object-oriented extensions to Lisp exist. Probably the most prominent in this category is Loops (developed at Xerox). Most Lisp implementations have well supported environments that provide for multiple and repeated inheritance, polymorphism and dynamic binding, and garbage collection. As with Lisp proper, types are not specified in the language.
Simula --Simula, an object-oriented extension of Algol 60, was developed in 1967 by Ole-Johan Dahl and Krysten Nygaard. Simula is a strongly typed language that supports program control blocks, coroutines, and the notion of a main program.
The major benefit brought to OOP by Simula is its set of built-in primitives for simulation modeling. In particular, Simula focuses on discrete event simulation where the model can be thought of as a state machine and the system consists of individual events. Each event is in a given state and the system evolves in response to these changing states.
Most implementations of Simula include a well-supported development environment and source-level debugging facilities. In addition, Simula supports the necessary garbage collection. In contrast with languages such as SmallTalk, however, Simula does not include a standard class library.
Although not a new idea, OOP has gained a great deal of popularity in recent years. One reason is that as hardware technology improves, systems can better handle the heavy demand on resources. The down side is that features such as multiple inheritance and dynamic binding can still slow down an application to a crawl. Waiting for improved hardware technology, then, is not the answer as we must look for new ways to optimize search strategies.
Remember too that every tool serves a specific purpose. Just as the carpenter has no use for a mechanic's wrench, you may find little use for object-oriented programming. But if you're involved in the development of large scale systems that must evolve over time, you'll want to take a look at the various object-oriented languages. In the process, however, keep in mind OOP's prime directive; reusability.
"Object-oriented Software Construction," Bertrand Meyer, Prentice Hall, 1988.
Copyright © 1989, Dr. Dobb's Journal