We are object-oriented creatures living in an object-oriented world. We tend to think in object-oriented ways.
For example, when planning a motor trip, we usually think first about the best way to get from point A to point B without being too concerned about how to get through the traffic in each of the cities along the way.
Once we are satisfied that we have the overall route mapped out appropriately, we may go back and begin thinking about the details, such as how to avoid five-oclock traffic in a particular city, where is the best place to stop for the night, is there some particular restaurant that we want to visit, and if so, how can we arrange the timing so as to arrive there at dinner time, etc. This is object-oriented thinking.
Previous approaches to programming (pre-OOP) tend to separate the data from the methods used to manipulate that data, or at least don’t strongly encourage them to be considered in concert.
The world and its applications are not organized into values and procedures separate from one another. People who solve problems in other crafts do not perceive the world that way. They deal with their problem domains by concentrating on the objects and letting the characteristics of those objects determine the procedures to apply to them.
To build a house, fix a flat tire, or repair a carburetor, you first think about the object and its purpose and behavior. Then you select your tools and procedures. The solution fits the problem.
Any object-oriented language must support three very important concepts:
We use these three concepts extensively as we attempt to model the real-world problems that we are trying to solve with our object-oriented programs.
Consider the steering mechanism of a car as real-world an example of encapsulation. During the past eighty years or so, the steering mechanism has evolved into an object in the OOP sense. In particular, most of us know how to use the steering mechanism of an automobile without having any idea whatsoever how it is implemented. All most of us care about is the interface which we refer to as a steering wheel. We know that if we turn the steering wheel clockwise, the automobile will turn to the right, and if we turn it counterclockwise, the car will turn to the left.
Most of us don’t know, and don’t really care, how the steering mechanism is actually implemented “under the hood.” In fact, there are probably a number of different implementations for various brands and models of automobiles. Regardless of the brand and model, however, the human interface is pretty much the same. Clockwise turns to the right, counterclockwise turns to the left.
To appreciate the importance of this standard interface, attach a short rental trailer to your car and try backing it into your driveway. Turning the steering wheel counterclockwise causes the trailer to turn to the right and clockwise causes the trailer to turn to the left; just the opposite from the above. Most of us aren’t accustomed to this interface and have some difficulty using it, at least initially. It is probably safe to suggest that the human factors aspect of the interface to the steering mechanism in your car wasn’t designed for backing up with a trailer attached. (The trick to adapting the interface is to put you hand on the bottom of the steering wheel instead of the top.)
In any event, as in the steering mechanism for a car, a common approach in OOP is to “hide the implementation” and “expose the interface” through encapsulation.
Another important aspect of OOP is inheritance. Let’s form an analogy with the teenager who is building a hotrod. That teenager doesn’t normally start with a large chunk of steel and carve an engine out of it. Rather, the teenager will usually start with an existing engine and make improvements on it. In OOP lingo, that teenager extends the existing engine, derives from the existing engine, inherits from the existing engine, or subclasses the existing engine.
Just like in “souping up” an engine for a hotrod, a very common practice in OOP is to create new improved objects using new definitions that extend existing definitions. In fact, one of the major arguments in favor of OOP is that it provides a formal mechanism which encourages the reuse of existing programming elements. One of the mottos of OOP is: reuse, don’t reinvent.
A third important aspect of OOP is polymorphism. This is a greek word meaning something like one name, many forms. This is a little more difficult to explain in non-programming terminology. However, we will stretch our imagination a little and say that polymorphism is somewhat akin to the automatic transmission in your car. In my Honda, for example, the automatic transmission has four different methods or functions known collectively as Drive (in addition to the functions of Reverse, Park, and Neutral).
As an operator of the automobile, I simply select Drive (meaning go forward). Depending on various conditions at runtime, the automatic transmission system decides which version of the Drive function to use in every specific situation. The specific version of the function that is used is based on the current conditions. This is somewhat analogous to what we will later refer to as runtime polymorphism.
I also believe it is true that my Honda has only one method which we refer to as Reverse. Once I select Reverse, that one method gets used. There is no automatic selection among multiple reverse methods. Therefore, my Honda exhibits polymorphic behavior when going in the forward direction, but exhibits non-polymorphic behavior when going backwards.