Object-Oriented
Programming
Object-oriented programming
(OOP) is at the core of Java. In fact, all Java programs are to at least some
extent object-oriented. OOP is so integral to Java that it is best to
understand its basic principles before you begin writing even simple Java
programs. Therefore, this chapter begins with a discussion of the theoretical
aspects of OOP.
Two
Paradigms
All computer programs consist
of two elements: code and data. Furthermore, a program can be conceptually
organized around its code or around its data. That is, some programs are
written around “what is happening” and others are written around “who is being
affected.” These are the two paradigms that govern how a program is
constructed. The first way is called the process-oriented
model. This approach characterizes a program as a series of linear steps
(that is, code). The process-oriented model can be thought of as code acting on data. Procedural languages such as C employ this model to
considerable success. However, as
mentioned in Chapter 1, problems with this approach appear as programs grow
larger and more complex.
To manage increasing
complexity, the second approach, called object-oriented
programming, was conceived. Object-oriented programming organizes a program
around its data (that is, objects) and a set of well-defined interfaces to that
data. An object-oriented program can be characterized as data controlling access to code. As you will see, by switching the
controlling entity to data, you can achieve several organizational benefits.
Abstraction
An essential element of
object-oriented programming is abstraction.
Humans manage complexity through abstraction. For example, people do not think
of a car as a set of tens of thousands of individual parts. They think of it as
a well-defined object with its own unique behavior. This abstraction allows
people to use a car to drive to the grocery store without being overwhelmed by
the complexity of the parts that form the car. They can ignore the details of
how the engine, transmission, and braking systems work. Instead, they are free
to utilize the object as a whole.
A
powerful way to manage abstraction is through the use of hierarchical
classifications. This allows you to layer the semantics of complex systems,
breaking them into more manageable pieces. From the outside, the car is a
single object. Once inside, you see that the car consists of several
subsystems: steering, brakes, sound system, seat belts, heating, cellular
phone, and so on. In turn, each of these subsystems is made up of more
specialized units. For instance, the sound system consists of a radio, a CD
player, and/or a tape or MP3 player. The point is that you manage the
complexity of the car (or any other complex system) through the use of
hierarchical abstractions.
Hierarchical
abstractions of complex systems can also be applied to computer programs. The
data from a traditional process-oriented program can be transformed by
abstraction into its component objects. A sequence of process steps can become
a collection of messages between these objects. Thus, each of these objects
describes its own unique behavior. You can treat these objects as concrete
entities that respond to messages telling them to do something. This is the
essence of object-oriented programming.
Object-oriented
concepts form the heart of Java just as they form the basis for human
understanding. It is important that you understand how these concepts translate
into programs. As you will see, object-oriented programming is a powerful and
natural paradigm for creating programs that survive the inevitable changes
accompanying the life cycle of any major software project, including
conception, growth, and aging. For example, once you have well-defined objects
and clean, reliable interfaces to those objects, you can gracefully
decommission or replace parts of an older system without fear.
The
Three OOP Principles
All object-oriented
programming languages provide mechanisms that help you implement the
object-oriented model. They are encapsulation, inheritance, and polymorphism.
Let’s take a look at these concepts now.
Encapsulation
Encapsulation is the mechanism that binds together code and the data it
manipulates, and keeps both safe from
outside interference and misuse. One way to think about encapsulation is as a
protective wrapper that prevents the code and data from being arbitrarily
accessed by other code defined outside the wrapper. Access to the code and data
inside the wrapper is tightly controlled through a well-defined interface. To
relate this to the real world, consider the automatic transmission on an
automobile. It encapsulates hundreds of bits of information about your engine,
such as how much you are accelerating, the pitch of the surface you are on, and
the position of the shift lever. You, as the user, have only one method of
affecting this complex encapsulation: by moving the gear-shift lever. You can’t
affect the transmission by using the turn signal or windshield wipers, for
example. Thus, the gear-shift lever is a well-defined (indeed, unique)
interface to the transmission. Further, what occurs inside the transmission
does not affect objects outside the transmission. For example, shifting gears
does not turn on the headlights! Because an automatic transmission is
encapsulated, dozens of car manufacturers can implement one in any way they
please. However, from the driver’s point of view, they all work the same. This
same idea can be applied to programming. The power of encapsulated code is that
everyone knows how to access it and thus can use it regardless of the
implementation details—and without fear of unexpected side effects.
In Java,
the basis of encapsulation is the class. Although the class will be examined in
great detail later in this book, the following brief discussion will be helpful
now. A class defines the structure
and behavior (data and code) that will be shared by a set of objects. Each
object of a given class contains the structure and behavior defined by the
class, as if it were stamped out by a mold in the shape of the class. For this
reason, objects are sometimes referred to as instances of a class. Thus, a class is a logical construct; an
object has physical reality.
When you
create a class, you will specify the code and data that constitute that class.
Collectively, these elements are called members
of the class. Specifically, the data defined by the class are referred to as member variables or instance variables. The code that operates on that data is referred
to as member methods or just methods. (If you are familiar with
C/C++, it may help to know that what a Java programmer calls a method, a C/C++ programmer calls a function.) In properly written Java
programs, the methods define how the member variables can be used. This means that the behavior and interface of a class
are defined by the methods that operate on its instance data.
Since the purpose of a class
is to encapsulate complexity, there are mechanisms for hiding the complexity of
the implementation inside the class. Each method or variable in a class may be
marked private or public. The public
interface of a class represents everything that external users of the class
need to know, or may know. The private
methods and data can only be accessed by code that is a member of the class.
Therefore, any other code that is not a member of the class cannot access a
private method or variable. Since the private members of a class may only be
accessed by other parts of your program through the class’ public methods, you
can ensure that no improper actions take place. Of course, this means that the
public interface should be carefully designed not to expose too much of the
inner workings of a class (see Figure 2-1).
Inheritance
Inheritance is the process by which one object acquires the properties of
another object. This is important
because it supports the concept of hierarchical classification. As mentioned
earlier, most knowledge is made manageable by hierarchical (that is, top-down)
classifications. For example, a Golden Retriever is part of the classification dog, which in turn is part of the mammal class, which is under the larger
class animal. Without the use of
hierarchies, each object would need
to define all of its characteristics explicitly. However, by use of
inheritance, an object need only define those qualities that make it unique
within its class. It can inherit its general attributes from its parent. Thus,
it is the inheritance mechanism that makes it possible for one object to be a
specific instance of a more general case. Let’s take a closer look at this
process.
Most people naturally view
the world as made up of objects that are related to each other in a
hierarchical way, such as animals, mammals, and dogs. If you wanted to describe
animals in an abstract way, you would say they have some attributes, such as
size, intelligence, and type of skeletal system. Animals also have certain
behavioral aspects; they eat, breathe, and sleep. This description of attributes
and behavior is the class definition for animals.
If you
wanted to describe a more specific class of animals, such as mammals, they
would have more specific attributes, such as type of teeth and mammary glands.
This is known as a subclass of
animals, where animals are referred to as mammals’ superclass.
Since
mammals are simply more precisely specified animals, they inherit all of the attributes from animals. A deeply inherited
subclass inherits all of the attributes from each of its ancestors in the class hierarchy.
Inheritance interacts with
encapsulation as well. If a given class encapsulates some attributes, then any
subclass will have the same attributes plus
any that it adds as part of its specialization (see Figure 2-2). This is a key
concept that lets object-oriented programs grow in complexity linearly rather
than geometrically. A new subclass inherits all of the attributes of all of its
ancestors. It does not have unpredictable interactions with the majority of the
rest of the code in the system.
Polymorphism
Polymorphism (from Greek, meaning “many forms”) is a feature that allows one
interface to be used for a general
class of actions. The specific action is determined by the exact nature of the
situation. Consider a stack (which is a last-in, first-out list). You might
have a program that requires three types of stacks. One stack is used for
integer values, one for floating-point values, and one for characters. The
algorithm that implements each stack is the same, even though the data being
stored differs. In a non–object-oriented language, you would be required to
create three different sets of stack routines, with each set using different
names. However, because of polymorphism, in Java you can specify a general set
of stack routines that all share the same names.
More
generally, the concept of polymorphism is often expressed by the phrase “one
interface, multiple methods.” This means that it is possible to design a
generic interface to a group of related activities. This helps reduce
complexity by allowing the same interface to be used to specify a general class of action. It is the
compiler’s job to select the specific
action (that is, method) as it applies to each situation. You, the programmer,
do not need to make this selection manually. You need only remember and utilize
the general interface.
Extending
the dog analogy, a dog’s sense of smell is polymorphic. If the dog smells a
cat, it will bark and run after it. If the dog smells its food, it will
salivate and run to its bowl. The same sense of smell is at work in both
situations. The difference is what is being smelled, that is, the type of data
being operated upon by the dog’s nose! This same general concept can be
implemented in Java as it applies to methods within a Java program.
Polymorphism,
Encapsulation, and Inheritance Work Together
When properly applied,
polymorphism, encapsulation, and inheritance combine to produce a programming
environment that supports the development of far more robust and scaleable
programs than does the process-oriented model. A well-designed hierarchy of
classes is the basis for reusing the code in which you have invested time and
effort developing and testing. Encapsulation allows you to migrate your
implementations over time without breaking the code that depends on the public
interface of your classes. Polymorphism allows you to create clean, sensible,
readable, and resilient code.
Of the
two real-world examples, the automobile more completely illustrates the power
of object-oriented design. Dogs are fun to think about from an inheritance
standpoint, but cars are more like programs. All drivers rely on inheritance to
drive different types (subclasses) of vehicles. Whether the vehicle is a school
bus, a Mercedes sedan, a Porsche, or the family minivan, drivers can all more
or less find and operate the steering wheel, the brakes, and the accelerator.
After a bit of gear grinding, most people can even manage the difference
between a stick shift and an automatic, because they fundamentally understand
their common superclass, the transmission.
People
interface with encapsulated features on cars all the time. The brake and gas
pedals hide an incredible array of complexity with an interface so simple you can
operate them with your feet! The implementation of the engine, the style of
brakes, and the size of the tires have no effect on how you interface with the
class definition of the pedals.
The
final attribute, polymorphism, is clearly reflected in the ability of car
manufacturers to offer a wide array of options on basically the same vehicle.
For example, you can get an antilock braking system or traditional brakes,
power or rack-and-pinion steering, and 4-, 6-, or 8-cylinder engines. Either
way, you will still press the brake pedal to stop, turn the steering wheel to
change direction, and press the accelerator when you want to move. The same
interface can be used to control a number of different implementations.
As you can see, it is through the application of encapsulation, inheritance, and polymorphism that the individual parts are transformed into the object known as a car. The same is also true of computer programs. By the application of object-oriented principles, the various parts of a complex program can be brought together to form a cohesive, robust, maintainable whole.
As mentioned at the start of this section, every
Java program is object-oriented. Or, put more precisely, every Java program
involves encapsulation, inheritance, and polymorphism. Although the short
example programs shown in the rest of this chapter and in the next few chapters
may not seem to exhibit all of these features, they are nevertheless present.
As you will see, many of the features supplied by Java are part of its built-in
class libraries, which do make extensive use of encapsulation, inheritance, and
polymorphism.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.