Object-Oriented Programming: What's it all about

I -- Intro

The Object Oriented trinity that all programmers are familiar with is:

Often, this trinity of concepts is invoked as if they explained object-oriented design, or the need for such a methodology. Encapsulation, inheritance and design, however, are elements of OO design; they do not explain OO design. I thought I would point that out because there seems to be some confusion surrounding this point. OO Analysis and Design is the process of modelling software solutions through visualizing a problem as a set of discete objects. The "why" of the problem, i.e., why do we model solutions to problems as if we were dealing with a set of objects rather than a set of procedures can be explained by examining the nature of the problems that we often deal with.

First, let's demystify object-oriented programming. And during its early days, and to some extent, even today, the very words "object-oriented" conjured ideas of some lab coat wearing mad scientist, basked in the glow of a bank of monitors, gleefully rubbing his hands while bringing life to some alchemical software "object." The more sober amongst us realized from the start that object-oriented software design was really just another software design technique. In engineering terminology, software design technique is now described as a paradigm. That, in itself, can be confusing, especially because modern engineers tend to be a fairly illiterate bunch. If we use a term, then we must understand it. Otherwise, we are lost. The word paradigm is Greek and translates as example or pattern. It could also be described as being a perfect example or exemplar. Now, if we perform a substitution and say, "The object-oriented example or pattern" rather than the object-oriented paradigm, are we gaining anything in understanding? I'm not so sure. It seems that what we really mean to say is, the object-oriented software development technique. But that may be just too big a mouthful, and so we are stuck with paradigm.

Before object-oriented design, there was procedural design -- also referred to as algorithmic design. The great procedural tool was the flowchart. IF I get in my car, AND I turn the key in the ignition, AND the car is fueled, THEN I can drive it to Walmart to pick up cat food (which will make the cat very happy). (Note: the automobile is a paradigm of object-oriented metaphors. All experienced programmers have used the automobile to describe object-oriented programming. It seems to work, so what the hell.) So, with procedural programming, we have to think about cause and event. What happens if I do this after I have done that? That aspect of programming has not disappeared. Not even from object-oriented programming.

There are more than two styles of programming, and for a given problem, one may be more appropriate than another. This list is derived from Grady Booch:


II -- A few definitions


How does object-oriented programming differ? Inheritance is, in my opinion, the most important aspect of object-oriented programming, and encapsulation is a close second. Polymorphism has more to do with syntactical operations. Quickly, polymorphism says, "Gee, wouldn't it be nice if I could give two or more functions that basically do the same thing the same name." We'll revisit this topic in more detail shortly.

Encapsulation says, "Gee, wouldn't it be nice if my data weren't inadvertently changed by myself or some other programmer". What we really want to do here is tie data to an object and be able to specify some degree of insurance against outside agents messing with it. We'll return to this also.

So, that leaves us with inheritance, which I said was the most important aspect of object-oriented programming. Well, I'm going to cover my ass, and say that it is arguably the most important aspect of object-oriented programming. Hey, it's a big field, and there's room for more than one opinion.

What does inheritance say? Inheritance says, "Hmmm, when I look at the wooded area behind my home, I see a tree. I know it is a tree because I have seen other trees and I know that trees share certain characteristics such as they all have trunks with bark and leaves or needles and that they have branches." And so on. First, it is obvious that this explanation is more complex than polymorphism or encapsulation. The reason, is that the term inheritance represents a relationship between entities. In OO terminology, inheritance denotes an "is-a" relationship between classes. If I look at a particular pine in my back yard and I can say that the pine is-a tree, then I may be looking at an inheritance situation.

III -- Design


Object-oriented programming differs conceptually from procedural programming. With the C++ language, we model systems using the class convention. We could, for example, say that there is a class of trees, and that class of trees would have the attributes that belong to a tree. Of course, as programmers, we have to design that class and that means deciding what attributes a tree has. This can be quite difficult.

Syntactically, if we were to create a Tree class, we would be creating what is called a user-defined data type, or an abstract data type.

The destinction between a user-defined data type and a native data type would be the difference between the Tree class that we are about to create and the int data type.

C and C++ give us some very basic data types, such as int, float, char and various pointers, etc. These are defined for us because they represent the most fundamental building blocks on which a program will be built and as such, their inclusion in a programming language is a practical necessity. So the chief distinction between a class and an int is the fact that the class is a type that is not supplied as part of the language. Bjarne Stroustrup, the creator of the C++ language, explains it best by saying that, "A type is a concrete representation of a concept." Programmers should keep this in mind when they are designing a class.

Oh gentle reader, bear with me...this paragraph is on its way to a better home...I am a slow worker and have not got to it yet... :-) New to C++, and something most C programmers have had to create every time they started a new project, is the bool type. C++ also has references , which are "like" pointers, but it is far more appropriate to say that a reference is an alias for an object. Or, as Stroustrup says, "A reference is an alternative name for an object." (The C++ Language, p. 61)

So, back to trees and what do trees have to do with inheritance, which I have proposed is the most important feature of object-oriented programming (and by extension, C++).

Here, we will not worry too much about the syntax of creating a class hierarchy. Syntax will be handled in other sections. I would also like to remind the reader that object-oriented programming, like procedural programming, is about modelling and designing software solutions to problems.

What is a tree? A tree is an idea. I cannot climb the idea of a tree. I can climb a particular pine tree that may be growing near my house. Similarly, I cannot live in the blueprint of a house. I can only live in a specific house in a specific neighborhood (and, hopefully one that I have a title to). Trees and houses are ideas. There are many types of trees and among those many types, there are many particular instances. So, there are trees, and there are pines, elms, and oaks. And there are various types of pines, elms, and oaks. And then, there is the specific pine tree that is near my house. That final pine tree differs from all the others in that it is not a classification of tree, but a specific tree.

Here is a simple example of what a tree class may look like in C++. Before we go any further, I'll admit that I don't know much about trees, and that this class won't do much for anyone other than serve as an example of the idea of inheritance using C++ syntax.

class Tree {
public : //"public" keyword is an access specifier
Tree( ); //this is a constructor
virtual ~Tree( ); //this is a virtual destructor
virtual unsigned int ageOfTree( ) = 0; //this is a pure virtual function
virtual unsigned int sizeOfTree( ); //this is a virtual function
protected : //another access specifier
unsigned int treeAge; //member data
unsigned int treeDiameter; //member data
unsigned int treeHeight; //member data
};

I'm going to get into things like virtual functions and pure virtual functions pretty soon. For now, let's say that you declare a function to be virtual if you think another class will be derived from the class in which it is declared. A pure virtual function basically guarantees that a given class will be derived from whatever class the pure virtual function appears in. This is because a class that has one or more pure virtual functions cannot be instantiated. (By the way, C programmers may be interested in knowing that the only difference between a class and a struct (in C++) is that a struct is public by default, whereas a class is private by default.)

If we take the Tree class as our base class, then a pine tree might look something like this:

class Pine : public Tree{ //this says a Pine "isa" Tree
public :
Pine( ); //Pine's constructor
virtual ~Pine( ); //another virtual destructor
virtual unsigned int ageOfTree( );
bool isDeciduous( const &Tree aTree ); //isDeciduous is a member function that takes

//a const reference to Tree and returns a bool

private :
unsigned int noOfBranches; //is something anyone would want to know?
char someOtherTreeInfo; //member data
};

So here is our first rather simple example of inheritance. In the first line of the declaration, we show that class Pine inherits all the non-private members of the Tree class. As you may have noticed, the base Tree class has no private members, so Pine inherits everything except the constructor. Constructors cannot be inherited and they cannot be declared to be virtual. However, it is possible to simulate a virtual constructor. Why you might want to do something like that is covered in James Coplien's Advanced C++. Because the Pine class is derived from the Tree class, and there are no private data or member functions in the Tree class, Pine gets the whole enchilada. There is no need to define a function sizeOfTree( ) because it exists in the base class. It is necessary, however, to define ageOfTree( ) because the base class declares that as a pure virtual function. That means that if you instantiate Pine, then you get a sizeOfTree( ) function. You also get treeAge, treeDiameter, and treeHeight. You don't get ageOfTree( ), but you will be forced to create such a function because pure virtual functions are placeholders. They tell whoever is extending the interface that ageOfTree( ) belongs in the interface of any class derived from Tree.

Let's forget the syntax and think about what we can achieve with inheritance. First, as just about every text book on object-oriented programming will tell you, a class that is derived from another class "is-a" (or "isa") type of that class. Thus, a Pine is-a Tree. (The reverse is not necessarily true; a Tree does not have to be a pine).

1