What is Object-Oriented Programming?
Any object-oriented program like a Java program, is a collection of interacting objects that models a collection of real-world objects. Think of the model that a kitchen designer might use to lay out your new kitchen. It will contain objects that represent the various kitchen appliances and cabinets. Each object in the model is a simplified version of the corresponding real object. For example, a rectangle might be used to represent the refrigerator.
A kitchen model is mostly static. It doesn’t change. Once put into place, its various objects just stand there in a certain relation to each other. By contrast, a computer program is dynamic. It changes. It does things and performs certain actions. The objects in a computer program communicate with each other, and they change over time. In this respect, the objects that make up our computer programs are very anthropomorphic, a big word that means “like people.” If we are eating together and I want you to pass me the salt, I say, “Please pass me the salt,” and you invariably comply. Similarly, when you (Student X) put your ATM card into an ATM machine, the ATM object asks the bank’s database object, “Give me Student X’s bank account object,” and the database invariably complies. If you tell the ATM you want to withdraw $100, it tells your bank account object to deduct $100 from your current balance. And so it goes. Both you and your bank account are changed objects as a result of the transaction.
What Is an Object?
So what is an object? Just as in the real world, an object is any thing whatsoever. An object can be a physical thing, such as a Car, or a mental thing, such as an Idea. It can be a natural thing, such as an Animal, or an artificial, human-made thing, such as an ATM. A program that manages an ATM would involve BankAccounts and Customer objects. A chess program would involve a Board object and ChessPiece objects.
Throughout this text, we will use the notation shown in figure below to depict objects and to illustrate object-oriented concepts. The notation is known as the Unified Modeling Language, or UML for short, and it is a standard in the object-oriented programming community. As the diagram shows, an object is represented by a rectangle whose label consists of the object’s (optional) id and its type. An object’s id is the name by which it is referred to in the computer program. In this case we show an ATM object whose id is not given and a ChessPiece object named pawn1. An object’s label is always underlined.
Attributes and Values
Just as with real objects, the objects in our programs have certain characteristic attributes. For example, an ATM object would have a current amount of cash
that it could dispense. A ChessPiece
object might have a pair of row
and column
attributes that specify its position on the chessboard. Note that an object’s attributes are themselves objects. The ATM’s cash
attribute and the chess piece’s row
and column
attributes are Number
s.
Figure below shows two ATM objects and their respective attributes. As you can see, an object’s attributes are listed in a second partition of the UML diagram. Note that each attribute has a value. So the lobby:ATM
has a $8650.00 in cash, while the drivethru:ATM
has only $150.00 in cash.
We sometimes refer to the collection of an object’s attributes and values as its state. For example, the current state of the lobby:ATM
is $8650.00 in cash. Of course, this is a gross simplification of an ATM’s state, which would also include many other attributes. But it illustrates the point for you.
Actions and Messages
In addition to their attributes, objects also have characteristic actions or behaviors. As we have already said, objects in programs are dynamic. They do things or have things done to them. In fact, programming in Java is largely a matter of getting objects to perform certain actions for us. For example, in a chess program the ChessPiece
s have the ability to moveTo()
a new position on the chessboard. Similarly, when a customer pushes the “Current Balance” button on an ATM machine, this is telling the ATM to report()
the customer’s current bank balance. (Note how we use parentheses to distinguish actions from objects and attributes.)
The actions that are associated with an object can be used to send messages to the objects and to retrieve information from objects. A message is the passing of information or data from one object to another. Figure below illustrates how this works. In UML, messages are represented by arrows. In this example, we are telling pawn1:ChessPiece
to moveTo(3,4)
. The numbers 3 and 4 in this case are arguments that tell the pawn what square to move to. (A chess board has eight rows and eight columns, and each square is identified by its row and column coordinates.) In general, an argument is a data value that specializes the content of a message in some way. In this example we are telling the pawn to move forward by one row. If we wanted the pawn to move forward by two rows, we would send the message moveTo(4,4)
.
The diagram in the figure below depicts a sequence of messages representing an idealized ATM transaction. First, an ATM customer asks the ATM machine to report his current balance. The ATM machine in turn asks the customer’s bank account to report the customer’s balance. The ATM receives the value $528.52 from the bank account and passes it along to the customer. In this case, the message does not involve an argument. But it does involve a result. A result is information or data that is returned to the object that sent the message.
Obviously, in order to respond to a message, an object has to know how to perform the action that is requested. The pawn has to know how to move to a designated square. The ATM has to know how to find out the customer’s current balance. Indeed, an object can only respond to messages that are associated with its characteristic actions and behaviors. You can’t tell an ATM to move forward two squares. And you can’t ask a chess piece to tell you your current bank balance.
Responding to a message or performing an action sometimes causes a change in an object’s state. For example, after performing moveTo(3,4)
, the pawn will be on a different square. Its position will have changed. On the other hand, some messages (or actions) leave the object’s state unchanged. Reporting the customer’s bank account balance doesn’t change the balance.
What Is a Class?
A class is a template for an object. A class encapsulates the attributes and actions that characterize a certain type of object. In an object-oriented program, classes serve as blueprints or templates for the objects that the program uses. We say that an object is an instance of a class. A good analogy here is to think of a class as a cookie cutter and its objects, or instances, as individual cookies. Just as we use the cookie cutter to stamp out cookies of a certain type, in an object-oriented program, we use a definition of a class to create objects of a certain type.
Writing an object-oriented program is largely a matter of designing classes and writing definitions for them in Java. Designing a class is a matter of specifying all of the attributes and behaviors that are characteristic of that type of object.
For example, suppose we are writing a drawing program. One type of object we would need for our program is a rectangle. A Rectangle
object has two fundamental attributes, a length
and a width
. Given these attributes, we can define characteristic rectangle actions, such as the ability to calculate its area and the ability to draw itself. Identifying an object’s attributes and actions is the kind of design activity that goes into developing an object-oriented program.
Figure below shows a UML diagram of our Rectangle
class. Like the symbol for an object, a UML class symbol has up to three partitions. Unlike the UML object symbol, the label for a UML class only gives the class’s name and is not underlined. The second partition lists the class’s attributes, and the third partition lists the class’s actions. Our rectangle has four attributes. The first two, x
and y
, determine a rectangle’s position on a two-dimensional graph. The second two, length
and width
, determine a rectangle’s dimensions. Note that the attributes have no values. This is because the class represents a general type of rectangle. It specifies what all rectangles have in common, without representing any particular rectangle. Like a cookie cutter for a cookie, a class gives the general shape of an object. The content is not included.
Variables and Methods
Up to this point we have been using the terms attribute and action to describe an object’s features. We will continue to use this terminology when talking in general about objects or when talking about an object or class represented by a UML diagram.
However, when talking about a programming language, the more common way to describe an object’s features is to talk about its variables and methods. A variable, which corresponds to an attribute, is a named memory location that can store a certain type of value. You can think of a variable as a special container that can only hold objects of a certain type. For example, as the figure above shows, Rectangle
’s length
and width
are variables that can store a certain type of numeric value known as an int
. An int
value is a whole number, such as 76 or -5.
A method, which corresponds to an action or a behavior, is a named chunk of code that can be called upon, or invoked, to perform a certain predefined set of actions. For example, in our Rectangle
object, the calculateArea()
method can be called upon to calculate the rectangle’s area. It would do this, of course, by multiplying the rectangle’s length by its width. Similarly, the draw()
method can be invoked to draw a picture of the rectangle. It would take the actions necessary to draw a rectangle on the console.
Instance (Object) versus Class Variables and Methods
Variables and methods can be associated either with objects or their classes. An instance variable (or instance method) is a variable (or method) that belongs to an object. By contrast, a class variable (or class method) is a variable (or method) that is associated with the class itself. An example will help make this distinction clear.
An instance variable will have different values for different instances. For example, individual Rectangle
s will have different values for their length
, width
, x
, and y
variables. So these are examples of instance variables. The calculateArea()
method is an example of an instance method because it uses the instance’s current length and width values in its calculation. Similarly, the draw()
method is an instance method because it uses the object’s length and width to draw the object’s shape.
An example of a class variable would be a variable in the Rectangle
class that is used to keep track of how many individual Rectangles have been created. (Our drawing program might need this information to help manage its memory resources.) Suppose we name this variable nRectangles
, and suppose we add 1 to it each time a new Rectangle
instance is created.
An example of a method that is associated with a class is a special method known as a constructor. This is a method used to create an object. It is used to create an instance of a class. Calling a constructor to create an object is like pressing the cookie cutter into the cookie dough: the result is an individual cookie (object).
Figure below illustrates these concepts. Note that class variables are underlined in the UML diagram. We have modified the Rectangle
class to include its constructor method, which is named Rectangle(). It takes four arguments, representing the values that we want to give as the rectangle’s x
, y
, length
, and width
respectively. The Rectangle
class’s nRectangles
variable has a value of 2, representing that two Rectangle
instances have been created. These are shown as members of the Rectangle
class.
It won’t be obvious to you at this point, but nRectangles
is a value that has to be associated with the Rectangle
class, not with its instances. To see this, let’s imagine what happens when a new Rectangle instance is created. Figure below illustrates the process. When the Rectangle() constructor is invoked, its arguments (100, 50, 25, 10) are used by the Rectangle
class to create a Rectangle
object located at x=100, y=50 and with a length of 25 and width of 10. The constructor method also increases the value of nRectangles
by 1 as a way of keeping count of how many objects it has created.
Class Hierarchy and Inheritance
How are classes related to each other? In Java, and in any other object-oriented language, classes are organized in a class hierarchy. A class hierarchy is like an upside-down tree. At the very top of the hierarchy is the most general class. In Java, the most general class is the Object
class. The classes below Object
in the hierarchy are known as its subclasses. Since all of the objects we use in our programs belong to some class or other, this is like saying that all objects are Object
s.
Figure below illustrates the concept of a class hierarchy using the classes that we have described in this section. As you can see, the Object
class occurs at the top of the hierarchy. It is the most general class. It has features that are common to all Java objects. As you move down the hierarchy, the classes become more and more specialized. A Rectangle
is an Object
, but it contains attributes (length and width) that are common to all rectangles but not to other objects in the hierarchy. For example, an ATM
object does not necessarily have a length and a width. Note that we have added a Square
class to the hierarchy. A Square
is a special type of Rectangle
, one whose length equals its width.
Superclass and subclass
To introduce some important terminology associated with this kind of hierarchy, we say that the Rectangle
class is a subclass of the Object
class. The Square
class is a subclass of both Rectangle
and Object
. Classes that occur above a given class in the hierarchy are said to be its superclasses. Thus the Rectangle
class is a superclass of the Square
class. The Object
class is also a superclass of Square
. In general, we say that a subclass extends a superclass, meaning that it adds additional elements (attributes and/or methods) to those contained in its superclasses. We saw this in the case of the Square
class. It adds the feature that its length and width are always equal.
Another important concept associated with a class hierarchy is the notion of class inheritance, whereby a subclass inherits elements (attributes and/or methods) from its superclasses. To take an example from the natural world, think of the sort of inheritance that occurs between a horse and a mammal. A horse is a mammal. So horses inherit the characteristic of being warm-blooded by virtue of also being mammals. (This is different from the kind of individual inheritance whereby you inherit your mother’s blue eyes and your father’s black hair.)
Class inheritance
To illustrate how inheritance works, let’s go back to our chess program. There are several different types of ChessPiece
s. There are Pawns
, and Knights
, and Queens
and Kings
. Figure below illustrates the chess piece hierarchy. A pair of attributes that all chess pieces have in common is their row and column position on the chessboard. Because all chess pieces have these attributes in common, they are located at the top of the ChessPiece
hierarchy and inherited by all ChessPiece
subclasses. Of course, the row
and column
attributes are given different values in each ChessPiece
object.\
One of the actions that all chess pieces have in common is that they can moveTo()
a given square on the chessboard. But different types of chess pieces have different ways of moving. For example, a Bishop
can only move along the diagonals on the chessboard, whereas a Rook
can only move along a row or column on the chessboard. So, clearly, we can’t describe a moveTo()
method that will work for all ChessPieces. This is why we put the moveTo()
method in all of the ChessPiece
subclasses. The ChessPiece
class also has a moveTo()
method, but note that its name is italicized. This indicates that it cannot be completely defined at that level.
Finally, note that in chess, the king has certain special attributes and actions. Thus only the king can be put in check. This means that the king is under attack and in danger of being captured, thereby ending the game. Similarly, only the king has the ability to castle. This is a special move that a king can make together with one of its rooks under certain conditions. Thus, the reason we show the inCheck
attribute and castle()
action in the King class is because these are characteristics that are particular to Kings.
In this way, a class hierarchy represents a specialization of classes as you move from top to bottom. The most general class, ChessPiece
, is at the top of the hierarchy. Its attributes and methods are passed on to (inherited by) its subclasses. However, in addition to the attributes and methods they inherit from their superclasses, the subclasses define their own special attributes and methods. Each of the subclasses, Pawn
, Bishop
, and so on, represents some kind of specialization of the superclass. In this example, each of the subclasses has its own distinctive way of moving. And the King subclass has unique attributes and actions, inCheck
and castle()
.
Principles of Object-Oriented Design
As we have discussed, an object-oriented program is composed of many objects communicating with each other. The process of designing an object-oriented program to solve some problem or other involves several important principles:
Divide-and-Conquer Principle. Generally, the first step in designing a program is to divide the overall problem into a number of objects that will interact with each other to solve the problem. Thus, an object-oriented program employs a division of labor much like the one we apply in organizing many of our real-world tasks. This divide-and-conquer approach is an important problem-solving strategy.
Encapsulation Principle. Once the objects are identified, the next step involves deciding, for each object, what attributes it has and what actions it will take. The goal here is to encapsulate within each object the expertise needed to carry out its role in the program. Each object is a self-contained module with a clear responsibility and the tools (attributes and actions) necessary to carry out its role. Just as a dentist encapsulates the expertise needed to diagnose and treat a toothache, a well-designed object contains the information and methods needed to perform its role.
Interface Principle. In order for objects to work cooperatively and efficiently, we have to clarify exactly how they are to interact, or interface, with one another. An object’s interface should be designed to limit the way the object can be used by other objects. Think of how the different interfaces presented by a digital watch and an analog watch determine how the watches are used. In a digital watch, time is displayed in discrete units, and buttons are used to set the time in hours, minutes, and seconds. In an analog watch, the time is displayed by hands on a clock face, and time is set, less precisely, by turning a small wheel.
Information-Hiding Principle. In order to enable objects to work together cooperatively, certain details of their individual design and performance should be hidden from other objects. To use the watch analogy again, in order to use a watch we needn’t know how its time-keeping mechanism works. That level of detail is hidden from us. Hiding such implementation details protects the watch’s mechanism but does not limit its usefulness.
Generality Principle. To make objects as generally useful as possible, we design them not for a particular task but rather for a particular kind of task. This principle underlies the use of software libraries. As we will see, Java comes with an extensive library of classes that specialize in performing certain kinds of input and output operations. For example, rather than having to write our own method to print a message on the console, we can use a library object to handle our printing tasks.
Extensibility Principle. One of the strengths of the object-oriented approach is the ability to extend an object’s behavior to handle new tasks. This also has its analog in the everyday world. If a company needs sales agents to specialize in hardware orders, it would be more economical to extend the skills of its current sales agents instead of training a novice from scratch. In the same way, in the object-oriented approach, an object whose role is to input data might be specialized to input numeric data.
Abstraction Principle. Abstraction is the ability to focus on the important features of an object when trying to work with large amounts of information. For example, if we are trying to design a floor plan for a kitchen, we can focus on the shapes and relative sizes of the appliances and ignore attributes such as color, style, and manufacturer. The objects we design in our Java programs will be abstractions in this sense because they ignore many of the attributes that characterize the real objects and focus only on those attributes that are essential for solving a particular problem.
License
- (C) CodeAhoy.
- This is a derivative of Java, Java, JavaObject-Oriented Problem Solving by R. Morelli and R. Walde. Used under Creative Commons Attribution License CC-BY 4.0.