|
|
|
|
The progress of abstraction
All programming languages provide
abstractions. It can be argued that the complexity of the problems you’re
able to solve is directly related to the kind and quality of
abstraction. By “kind” I mean, “What is
it that you are abstracting?” Assembly language is a small abstraction of
the underlying machine. Many so-called “imperative” languages that
followed (such as Fortran, BASIC, and C) were abstractions of assembly language.
These languages are big improvements over assembly language, but their primary
abstraction still requires you to think in terms of the structure of the
computer rather than the structure of the problem you are trying to solve. The
programmer must establish the association between the machine model (in the
“solution space,” which
is the place where you’re modeling that problem, such as a computer) and
the model of the problem that is actually being solved (in the
“problem space,” which
is the place where the problem exists). The effort required to perform this
mapping, and the fact that it is extrinsic to the programming language, produces
programs that are difficult to write and expensive to maintain, and as a side
effect created the entire “programming methods”
industry.
The alternative to modeling the machine
is to model the problem you’re trying to solve. Early languages such as
LISP and APL chose particular views of the world (“All problems are
ultimately lists” or “All problems are algorithmic”). PROLOG
casts all problems into chains of decisions. Languages have been created for
constraint-based programming and for programming exclusively by manipulating
graphical symbols. (The latter proved to be too restrictive.) Each of these
approaches is a good solution to the particular class of problem they’re
designed to solve, but when you step outside of that domain they become awkward.
The object-oriented approach goes a step
farther by providing tools for the programmer to represent elements in the
problem space. This representation is general enough that the programmer is not
constrained to any particular type of problem. We refer to the elements in the
problem space and their representations in the solution space
as “objects.” (Of course, you will also need
other objects that don’t have problem-space analogs.) The idea is that the
program is allowed to adapt itself to the lingo of the problem by adding new
types of objects, so when you read the code describing the solution,
you’re reading words that also express the problem. This is a more
flexible and powerful language abstraction than what we’ve had before.
Thus, OOP allows you to describe the problem in terms of the problem, rather
than in terms of the computer where the solution will run. There’s still a
connection back to the computer, though. Each object looks quite a bit like a
little computer; it has a state, and it has operations that you can ask it to
perform. However, this doesn’t seem like such a bad analogy to objects in
the real world; they all have characteristics and behaviors.
Some language designers have decided that
object-oriented programming by itself is not adequate to easily solve all
programming problems, and advocate the combination of various approaches into
multiparadigm programming
languages.[4]
Alan Kay summarized five
basic characteristics of Smalltalk,
the first successful object-oriented language and one of the languages upon
which C++ is based. These characteristics represent a pure approach to
object-oriented
programming:
- Everything is an
object. Think of an object as a fancy variable;
it stores data, but you can “make requests” to that object, asking
it to perform operations on itself. In theory, you can take any conceptual
component in the problem you’re trying to solve (dogs, buildings,
services, etc.) and represent it as an object in your
program.
- A program is a bunch of objects
telling each other what to do by sending
messages. To make a request of an object, you
“send a message” to that object. More concretely, you can think of a
message as a request to call a function that belongs to a particular
object.
- Each object has its own memory made
up of other objects. Put another way, you create
a new kind of object by making a package containing existing objects. Thus, you
can build complexity in a program while hiding it behind the simplicity of
objects.
- Every object has a
type. Using the parlance, each object is an
instance of a class, in which “class” is synonymous
with “type.” The most important distinguishing characteristic of a
class is “What messages can you send to
it?”
- All objects of a particular type
can receive the same messages. This is actually
a loaded statement, as you will see later. Because an object of type
“circle” is also an object of type “shape,” a circle is
guaranteed to accept shape messages. This means you can write code that talks to
shapes and automatically handles anything that fits the description of a shape.
This substitutability is one of the most powerful concepts in
OOP.
|
|
|