Object-Oriented Development

Background Reading Material

As of the date of the original writing of this lesson (February 1998), I have not found any books that do a good job of explaining the object-oriented development process and illustrate that process using the Java programming language.

Some of the available books are highly theoretical and don't illustrate the process with any language (Object-Oriented Modeling and Design, by Rumbaugh, Blaha, Premerlani, Eddy, and Lorensen, for example)

Most of the books that I have found illustrate the development process using either C++ or Smalltalk. Since I teach C++ programming, and have no experience using Smalltalk, I have concentrated on the C++ books.

Even at that, the available books that explain the object-oriented development process are rather sparse. My bookshelf contains approximately 50 books on various aspects of programming using the C++ language, and only four or five address the object-oriented development process. Most concentrate on the various aspects of object-oriented programming while assuming that you know how to develop and design your programs in the first place.

Fortunately, C++ and Java are similar enough that much of what you will read about C++ will have a direct analogy in Java. However, the C++ books may make use of multiple inheritance which isn't supported by Java and the interface that is an important part of Java isn't supported by C++. Because of these differences, I am anxious to find a good book on object-oriented development that is illustrated using Java.

If you are a beginning programmer (or an experienced programmer just getting into object-oriented development), I recommend C++ Program Design, An Introduction to Programming and Object-Oriented Design by Cohoon and Davidson. This is a textbook that is targeted for a first programming course and is about the fundamentals of programming and software development.

However, rather than concentrating on the procedural programming style based on functional decomposition and top-down design as is the case with most textbooks written for a course at the beginning level, Cohoon and Davidson concentrate on object-oriented design. I applaud the work of these authors in this regard. It is my belief that if we were to teach OOD at the beginning, we could avoid the later trauma of requiring the students to "unlearn" functional decomposition in order to replace it by the more powerful object-oriented development. I personally believe that the first programming course at colleges and universities should be based on object-oriented development, but many of my colleagues in the field obviously don't agree.

As of February 1998, you can obtain information about this book at http://www.tme.com. Although I haven't checked, you can probably obtain information at www.amazon.com as well.

Another book that contains useful information on the topic is C++ and Object-Oriented Programming by Kip R. Irvine. This book has a short section in Chapter 5 entitled "Basic Principles of Object-Oriented Design" that explains the basic principles and also provides a good case study which walks through the design of a scheduling system for a doctor's clinic. A number of important aspects of OOD are illustrated by the case study.

A very good book on the topic is Mastering Object-Oriented Design in C++ by Cay S. Horstman. This book was written for a one-semester undergraduate course in object-oriented design and programming. This is a very readable book if you already know the C++ language, and if not, well you can learn the language along with the design concepts.

The last book on my list is UML and C++: A Practical Guide to Object-Oriented Development by Richard Lee and William Tepfenhart. According to the authors, this book was written "for busy professional software analysts and developers who work on large systems, especially those who need to integrate their new systems with legacy systems."

This book bridges the gap between the books containing mainly theory (such as the Rumbaugh book mentioned earlier) and the books that integrate theory with practical usage of the language throughout the book. The first eleven chapters of this book lay a theoretical foundation for object-oriented development based on the the Unified Modeling Language (UML). The remaining chapters illustrate the C++ implementation of designs produced using those theoretical methods.

I hasten to point out, however, that the first eleven chapters contain much more than just theory. In every chapter, the authors discuss the theory behind the concepts covered by that chapter, and in almost every case, end up the chapter with one or two sections that "cut through the theory" and provide their recommend approach to accomplishing the goals of the chapter in a real-world situation. They do a very good job of relating theory to the real world.

Let me also mention that this book by Lee and Tepfenhart contains the best real-world explanation of object-orientation that I have seen. If you don't do anything else, you should go to a library or bookstore and read the section of the book entitled "The Object-Oriented Way of Organizing Reality" that begins on page 32 and continues through page page 38.

The authors pose a very interesting scenario involving a family with several children and a dog that need to mow the lawn. By explaining the actions of the various family members in getting the lawn mowed, the authors illustrate many of the important concepts involved in object-oriented development. They also use the same scenario many times later in the book to illustrate various aspects of object-oriented development.

My plan is to tailor the information that I provide on object-oriented development in a format similar to that used by Lee and Tepfenhart. In particular, this lesson will provide a lightweight explanation of the object-oriented development process (for the heavyweight development stuff, you will need to refer to books written by the above authors or other authorities in the field such as Booch and Budd). Although I may include some Java code fragments in this lesson for illustration purposes, I won't provide any complete programs.

Later I will sprinkle several lessons containing sample programs developed using object-oriented methodology among the other lessons in the tutorials. That way, you will have an opportunity to study those lessons in the context of the other lessons that you are studying.

Introduction

A very brief description of Object-Oriented Design (OOD) was presented near the end of Lesson 4 along with an example of using OOD to develop a simple program that simulated a digital counter. If you haven't seen that, you might want to go back and review it.

Historical Perspective

You can read about historical perspectives in dozens of good books so I won't dwell on the topic here, but I will make a few comments to set the stage.

In the beginning (when I was writing Fortran programs as a young electrical engineer) there was lots of spigot code where the primary objective was to improve speed, reduce memory requirements, or both. In those days of mainframe computers, the charge for running a program was often based on the time required to run the program and the amount of system resource required to run the program. This produced code that was very difficult to maintain.

Later on we learned about other approaches including structured programming, functional decomposition, and top-down design. These were improvements to be sure. However, as the size of software systems continued to grow, this also produced software systems that were very difficult to maintain. To introduce some terminology that we will visit again later, these systems had strong coupling and poor cohesion.

Coupling refers to how tightly the various functions or procedures in the software system are tied together and how tightly they are tied to various areas of data storage.

Cohesion refers to how closely the data is associated with the functions that are allowed to manipulate it.

A software system with tight coupling and poor cohesion is a system where minor changes in one part of the software system ripple throughout the entire system resulting in requirements for extensive recoding in order to make minor modifications to the system.

Modular programming provided a solution to some of these problems but left some other problems unresolved. For example, if you use modular programming to implement a stack, you can reduce coupling and improve cohesion and things may be OK until you need two or more stacks in the same program.

Modular programming is unable to deal very well with the possibility that you might implement an algorithm (such as a stack) in a module and then need two or more entities that execute that algorithm and differ only in the data that those entities act upon.

In other words, your program might need two, three, or a dozen independent stacks and modular programming doesn't cope with that requirement very well. For example, in one of the sample programs that you will see later, I instantiate an array containing references to twenty-six different Hashtable objects. With OOP, that is extremely easy to do. It would be much more difficult with modular programming.

Object-oriented development was invented to deal with problems of this sort. A system that is properly designed using object-oriented methodology will have a low level of coupling and a high degree of cohesion, thus reducing maintenance and modification costs. Furthermore, once you develop a new type in an object-oriented program (such as a stack type) you can instantiate as many objects of that type as the program needs.

I assume that whether you agree with the concept or not, the fact that you are learning to program in Java means that you are reconciled to the prospect of programming with objects (since it's impossible to program in Java without at least the minimal use of objects).

Lesson 4 included many pages describing Object-Oriented Programming, but didn't provide much justification for its use. I will attempt in this lesson to provide some of the missing justification.

Why are so many companies encouraging their programmers to make the move toward object-oriented development? Probably the overriding reason is that software systems that are properly developed according to the object-oriented paradigm are more flexible, more extensible, and less costly to maintain and modify than systems developed according to the functional decomposition, top-down design approach that has been in use for twenty years or so.

Why are many experienced programmers resisting this change? Because change is hard in the best of circumstances, and is especially hard when you have based your entire career on a particular design methodology and are asked to change to a different methodology. Let's face facts. In many cases, changing to an entirely different design methodology eliminates the competitive advantage based solely on years of accumulated experience enjoyed by many older programmers such as myself. Suddenly we older programmers are having to compete with the youngsters in a game where most of our advantage has evaporated.

Be that as it may, I believe that the arguments for object-oriented development are so compelling that those of us who don't learn how to do it well will find ourselves technically "out to pasture" in a few short years.

In summary, according to Lee and Tepfenhart:
 
"the application (system) is a dynamic network of collaborating objects. 

... the object-oriented method allows software developers to manage the complexity of the problem domain and its supporting technology. When developers can manage more aspects of the problem domain, they can produce more flexible and more maintainable software."

.

Complexity

As mentioned earlier, in order to write software that is inexpensive to maintain and modify, that software must have a low degree of coupling and good cohesion. In object-oriented development, we accomplish that goal using the abstract mechanism of the class.

First, what do we mean by the word abstract? Abstraction is the process of exposing the essentials and ignoring the non-essentials. What is and what is not essential will depend on the problem domain. For example, the word dog is an abstraction for a class of four legged animals having certain characteristics. The essentials regarding a dog in the hunting domain are certainly different from the essentials regarding a dog at the dog shows put on by kennel clubs.

So we use the class as an abstraction mechanism to manage complexity in modeling real-world problems.

Abstraction has been used for many years in computer programming. In fact, the finished program is an abstraction of a real-world problem.

For example, functions and procedures were early forms of abstraction. Functions (such as square root functions) made it possible to collect commonly-needed capabilities into libraries and to use them at different points in a given program and even to use them in different programs.

Procedures or subroutines made it possible for programmers to write a chunk of code once and use it more than once within a program. Both functions and procedures or subroutines reduced or eliminated the requirement to duplicate code. My earliest recollection of Fortran programming involved the use of the math function library (although I'm not certain that is what it was called) and the ability to organize the program into subroutines.

To some extent, the use of functions and procedures made it possible to implement information hiding which has been recognized as a valuable programming mechanism for a good many years. However, as a practical matter, the information wasn't really hidden because it was accessible by functions and procedures other than those that were supposed to have access to the data. The object methodology provides the class and object mechanism which implements true information hiding.

The module was invented to solve the problem of information hiding and it did make it possible to accomplish true information hiding. However, as indicated earlier, while this was a step in the right direction, it didn't go far enough. If modular programming is used to implement an abstract data type, only one instance of the type is available. Modular programming did not provide instantiation capability which makes it possible to instantiate one or more entities of the new type. The object-oriented methodology deals with this problem by making it possible to instantiate any number of objects of a new abstract data type.

Abstract Data Types

What do we mean when we speak of abstract data types? According to Lee and Tepfenhart:
 
"An abstract data type is a programmer-defined data type that can be manipulated in a manner similar to predefined types."
Certainly we wouldn't be very happy if we could only use one variable of each of the predefined types in a program, because those types wouldn't be very useful under those circumstances. Similarly, an abstract data type isn't very useful if only one entity of that new type is available to the program.

What do we mean when we speak of a type? A type is defined by the set of data values that can be stored in an entity of that type and the set of operations that can be performed on an entity of the type. For example, in a language that supports an eight-bit unsigned integer type, the set of possible values for that type is the range of all whole numbers between between 0 and 255. The set of data values that can be stored in a variable of the type does not include negative values, does not include values with a fractional part, and does not include positive values greater than 255.

The set of possible operations that can be performed on a variable of the type may vary from one language to the next, but will probably include addition, subtraction, multiplication, and division, in addition to other operations commonly performed on integers.

In Java, whenever you define a class, you automatically a new abstract data type. Within that class definition, you define the set of values that can be stored in an entity of that type (commonly called an object) and you define the set of operations that can be performed on an object of the type defined by the new class.

We define the set of data values by defining instance variables of the class. We define the operations that can be performed on an object of the type by defining a set of methods for the class.

According to Lee and Tepfenhart, this mechanism gives us the ability to do the following:
 
  • Extend the programming language by adding programmer-defined type(s).
  • Make available to other code a set of programmer-defined functions that are used to manipulate the instance data of the specific programmer-defined type.
  • Protect (hide) the instance data associated with the type and limit access to the data, allowing access by only the programmer-defined functions.
  • Make (unlimited) instances of the programmer-defined type.
According to Lee and Tepfenhart, Object-Oriented Programming has added several new ideas to the concept of abstract data types. One of these is the idea of message passing.

Message Passing

Quoting Lee and Tepfenhart:
 
In object-oriented programming, an action is initiated by a service request (message) being sent to a specific object, not as in imperative programming by a function call using specific data.
What does this really mean? It means that the object that receives the message already owns some, or perhaps all of the data needed to satisfy the request. For example, a stack object owns all of the data contained in the stack at any point in time. A message to pop a data element from the stack doesn't require any additional data to be provided from outside the object. Obviously, a message to push new data onto the stack will require that the new data be provided along with the message.

On the other hand, with classical procedural programming, with the possible exception of static variables in languages like C and C++, all of the data required for a function to accomplish its purpose must be passed when the function call is made. The function doesn't really own any data of its own; hence poor cohesion.

Polymorphism

Another innovation that comes with object-oriented programming is the idea that different types of objects can behave differently on receipt of the same message (polymorphism). For example, when you learn to write programs that support the development of programs with a graphical user interface (GUI), you will learn that many different objects (known as listener objects) can register to be notified when the user clicks on a button. The result is that the button (known as the source of an event) will send the same message to all of the registered listener objects. The behavior of the event-handlers in each of the listener objects can be radically different from the behavior of the event-handlers in the other listener objects.

Furthermore, objects of the same type can respond differently at different points in time to the same message depending on the state of the object when the message is received. We will discuss this later in conjunction with our discussion of static and dynamic behavior.

Again, except for the possible use of static variables, a function in classical procedural programming doesn't have anything amounting to state. Even with the function name overloading that is available in C++, a global function with a given signature will always behave the same way whenever it is invoked, unless its behavior is somehow modified by the values of the data passed as parameters. The requirement to elicit different behavior from a function on the basis of the values of the parameters indicates poor cohesion.

Generalization/Specialization

In producing an object-oriented design, we are interested in discovering three characteristics of the problem domain, as a minimum:

A little later, we will discuss the question of what are the objects in some detail. For the moment, let's consider how they relate to one another.

Object-oriented programming uses the concept of generalization/specialization. This is one type of relationship. In Java, this is accomplished using inheritance. For example, in the following code fragment, the class named Programmer inherits from, or extends the class named Person.
 
class Person{

  //define members of the class

}//end class Super



class Programmer extends Person{

  //define members of the class

}//end class Sub
Generalization/specialization

Code reuse is encouraged, and development costs are reduced through the development and reuse of software components in much the same way that hardware components (such as standard microchips) has reduced the cost of hardware development.

The concept of generalization/specialization works together with polymorphism to support a low degree of coupling between individual software components (objects) along with good cohesion.

By the way, this type of relationship if often referred to as an is a relationship. In the above code fragment, an object of the Programmer class not only is a programmer, the object also is a Person.

Note however, that while extremely useful, the generalization/specialization type of relationship is not sufficient to satisfy all our relationship needs in modeling the problem. We need other mechanisms to help us model real-world relationships.

The Has-A Relationship

Lightweight treatments of OOD, such as this one, generally refer to one other kind of relationship between objects. (Heavyweight treatments take relationships much further.)

This relationship is often described as composition or contains. In particular, it means that an object of one type has a object of another type as one of its instance variables. For example, the following code fragment expands on the one above fragment to also include a has a relationship.
 
class Person{

  //define members of the class

}//end class Super



class Programmer extends Person{

  private Education educationLevel;

  //define other members of the class

}//end class Sub



class Education{

  //define members of the class

}//end class Education
Now an object of the class Programmer exhibits an is a relationship with the Person class and exhibits a has a relationship with the Education class. In other words, an object of the class Programmeris a Person and has a Education object as one of its members.