Chapter 21
In This Chapter
Abstracting away the details
Contrasting the object-oriented approach with the functional approach
Classifying things
Examples of objects abound in everyday life. Right in front of me is a chair, a table, a computer, and a red Starbucks mug. I have no trouble grouping these objects into taxonomies based on their properties. For example, the mug is a container, it’s also a thermal insulator, so I can use it to hold hot or cold things, and it has mass, so that I can use it as a paperweight or to throw at the dog. Object-oriented programming applies this view of the world to that of programming. To explain what I mean, let me start with a story.
Sometimes, when my son and I are watching football, I whip up a batch of nachos. Nothing fancy, mind you — I dump some chips on a plate, throw on refried beans, cheese, and a batch of jalapeños, and nuke the lot in the microwave oven for five minutes. To use the oven, I open the door, place the nachos inside, punch some buttons on the front, and hit start. After a few minutes, the bell rings to tell me they’re done. If I do something wrong, the oven beeps at me and doesn’t start. Sometimes it displays an error message on the little display.
This doesn’t sound very profound, and it isn’t really — until you consider all the things that I don’t do to use my microwave oven:
These are not profound observations. Humans can think about only so much at any one time. We tend to reduce the number of things that we have to deal with by abstracting away all the little details. This allows us to work at the level of detail appropriate to the problem we’re trying to solve.
Note: In object-oriented (OO) terms, this level of detail is known as the level of abstraction.
When I’m working on nachos, I view my microwave oven as a black box. I don’t concern myself with what’s going on inside that box unless, of course, it breaks. Then I might take the top off and see if I can figure out what’s wrong with it; then I’m working at a different level of abstraction. I still don’t take the tops off the computer chips on the circuit board or try to take apart the individual components. (I’m not that crazy.)
As long as the microwave is heating food, I limit myself to the interface that it exposes to the outside world: the keypad and LCD display. It is very important that from this interface, there’s nothing that I can do that will cause the microwave to
Suppose I were to ask my son to write an algorithm for making nachos using the same basic approach used for changing tires in Chapter 1. He would probably write something like, “Open a can of beans, grate some cheese, cut the jalapeños,” and so on. For the part about heating the nachos, he would write something similar to, “Cook in the oven until cheese is melted.”
That description is straightforward and complete, but it’s not how a procedural programmer would code a program to make nachos. Procedural programmers live in a world devoid of objects such as microwave ovens. They tend to worry about flowcharts (with their myriad functional paths). In a procedural solution, the flow of control would pass from my finger through the microwave’s front panel and on into the interior of the thing. Soon, the flow would be wiggling through complex logic paths concerned with how long to charge up some capacitor and whether it’s time to sound the “come and get it” tone.
In a world like this, it’s hard to think in terms of levels of abstraction. There are no objects, no abstractions behind which to hide inherent complexity.
In an object-oriented approach to making nachos, I would start by identifying the types of objects in the problem: chips, beans, cheese, and an oven. These are the nouns that I have to work with. That done, I would identify the verbs relevant to each object. Next, I would solve the problem using nothing but the nouns and verbs identified before. Finally, then, and only then, I would implement each of these objects in software.
While I’m writing object-level code, I’m working (and thinking) at the level of abstraction of the basic objects. I need to think about making a useful oven, but I don’t have to think about the process of making nachos yet. After all, the designers of my microwave didn’t think about the specific problem of my making a snack. Rather, they set about the problem of designing and building a useful microwave oven.
After I have successfully coded and tested the objects I need, I can ratchet up to the next level of abstraction. I can start thinking at the nacho-making level, rather than at the microwave-making level. At this point, I can pretty much translate my son’s instructions directly into C++ code.
Critical to the concept of abstraction is that of classification. If I were to ask my son, “What’s a microwave oven?” he would probably say, “It’s an oven that… .” If I then ask, “What’s an oven?” he might reply, “It’s a kitchen appliance that… .” I could keep asking this question, ratcheting myself up the abstraction ladder until I ended up with, “It’s a thing,” which is another way of saying, “It’s an object.”
My son understands that our particular microwave is an instance of the type of things called microwave ovens. In addition, he sees microwave ovens as just a special kind of oven, which is, in turn, a special type of kitchen appliance, and so on.
The technical way of saying all this is that our oven is an instance of the class microwave. The class microwave is a subclass of the class oven, and the class oven is a superclass of the class microwave.
Humans classify. Everything about our world is ordered into taxonomies. We do this to reduce the number of things that we have to remember. Consider, for example, the first time that you saw a hybrid car. The advertisement called it a “revolutionary automobile, unlike any car you’ve ever seen,” but you and I know that this just isn’t so. Sure, its propulsion system is different from conventional cars, but it’s still a car and as such does the same things that all cars do: convey you and your kin from one place to another. It has a steering wheel, seats, a motor, brakes, and so on. I bet I could even drive one without help.
I don’t have to clutter my limited storage with all the things that a hybrid card has in common with other cars. All I have to remember is that “a hybrid car is a car that …” and tack on those few things that are unique to a hybrid. Cars are a subclass of wheeled vehicles, of which there are other members, such as trucks and pickups. Maybe wheeled vehicles are a subclass of vehicles, which includes boats and planes. And on and on and on.
It may seem easier to design and build a microwave oven specifically for this one problem, rather than to build a separate, more generic oven-object. Suppose, for example, that I were to build a microwave to cook nachos and nachos only. I wouldn’t need to put a front panel on it, other than a START button. I always cook nachos the same amount of time. I could dispense with all that DEFROST and TEMP COOK nonsense. The microwave could be tiny. It would need to hold only one fat, little plate. Any more cubic feet of space would be completely wasted on nachos.
For that matter, suppose I just dispense with the concept of “microwave oven” altogether. All I really need is the guts of the oven. Then in the recipe, I can put the instructions to make it work: “Put nachos in the box. Connect the red wire to the black wire. Notice a slight hum. Don’t stand too close if you intend to have children.” Stuff like that.
Nevertheless, the procedural approach does have some problems:
It does cost more to write a generic object. It would be cheaper to build a microwave made specifically for nachos. You could dispense with expensive timers, buttons, and the like that aren’t needed to make nachos. After you have used a generic object in more than one application, however, the costs of a slightly more expensive class more than outweigh the repeated costs of building cheaper, less flexible classes for every new application.
Now, it’s time to reflect on what you’ve learned. Here’s what happens in an object-oriented approach to programming:
An integral part of this programming model is that each class is responsible for itself. A class should be in a defined state at all times. It should not be possible to crash the program by calling a class with illegal data or with an illegal sequence of correct data.
Many of the features of C++ that are shown in subsequent chapters deal with giving the class the capability to protect itself from errant programs just waiting to trip it up.
3.139.70.101