http://www.developer.com/

Back to article

Moving from Procedural to Object-Oriented Development


February 25, 2004

Last month's article introduced some basic object-oriented concepts as well as some of the differences between procedural and object-oriented technologies. Now, let's delve a bit deeper into these topics.

Procedural Programming

Procedural programming separates the data of the program from the operations that manipulate the data. For example, if you want to send information across a network, only the relevant data is sent (see Figure 1), with the expectation that the program at the other end of the network pipe knows what to do with it. In other words, some sort of handshaking agreement must be in place between the client and server to transmit the data. In this model, no code is actually sent over the wire.

Figure 1 - Data transmitted over a wire.

OO programming

The fundamental advantage of OO programming is that the data and the operations that manipulate the data (the code) are both encapsulated in the object. For example, when an object is transported across a network, the entire object, including the data and behavior, goes with it. In Figure 2, the Employee object is sent over the network.

Proper Design

A good example of this concept is a Web object, such as a Java applet. The browser has no idea of what the Web object will do. When the object is loaded, the browser executes the code within the object and uses the data contained within the object.


Figure 2 - Objects transmitted over a wire.

What Exactly Is an Object?

Objects are the building blocks of an OO program. A program that uses OO technology is basically a collection of objects. To illustrate, let's consider that a corporate system contains objects that represent employees of that company. Each of these objects is made up of the data and behavior described in the following sections.

Object Data

The data stored within an object represents the state of the object. In OO programming terminology, this data is called attributes. In our example, as shown in Figure 3, employee attributes could be Social Security number, date of birth, gender, phone number, and so on. The attributes contain the information that differentiates between the various objects, in this case the employees. Attributes are covered in more detail later in this series when classes are covered.

Figure 3 - Employee attributes.

Object Behaviors

The behavior of an object is what the object can do. In procedural languages, the behavior is defined by procedures, functions, and subroutines. In OO programming terminology, these behaviors are contained in methods, and you invoke a method by sending a message to it. In our employee example, consider that one of the behaviors required of an employee object is to set and return the values of the various attributes. Thus, each attribute would have corresponding methods, such as setGender() and getGender(). In this case, when another object needs this information, it can send a message to an employee object and ask it what its gender is.

Getters and Setters

The concept of getters and setters supports the concept of data hiding. Because other objects should not directly manipulate data within another object, the getters and setters provide controlled access to an object's data. Getters and setters are sometimes called accessor methods or mutator methods.

Note that we are only showing the interface of the methods, and not the implementation. The following information is all the user needs to know to effectively use the methods:

  • The name of the method
  • The parameters passed to the method
  • The return type of the method

To further illustrate behaviors, consider Figure 4.

In Figure 4, the Payroll object contains a method called CalculatePay() that calculates the pay for a specific employee. Among other information, the Payroll object must obtain the Social Security number of this employee. To get this information, the payroll object must send a message to the Employee object (in this case, the getSocialSecurityNumber() method). Basically, this means that the Payroll object calls the getSocialSecurityNumber() method of the Employee object. The employee object recognizes the message and returns the requested information.

Figure 4 - Employee behaviors.

To illustrate further, Figure 5 is a class diagram representing the Employee/Payroll system we have been talking about.

Figure 5 - Employee and payroll class diagrams.

UML Class Diagrams

Because this is the first class diagram we have seen, it is very basic and lacks some of the constructs (such as constructors) that a proper class should contain. Fear not; we will discuss class diagrams and constructors in more detail later in the series.

Each class diagram is broken up into two separate sections (besides the name itself). The first section contains the data (attributes), and the second section contains the behaviors (methods). In Figure 5, the Employee class diagram's attribute section contains SocialSecurityNumber, Gender, and DateofBirth, whereas the method section contains the methods that operate on these attributes. You can use programming tools such as Rational Rose or Borland Together to create and maintain class diagrams that correspond to real code.

Modeling Tools

Rational Rose and Borland Together are visual modeling tools that provide a mechanism to create and manipulate class diagrams by using the Unified Modeling Language (UML).

We will get into the relationships between classes and objects later, but for now you can think of a class as a template from which objects are made. When an object is created, we say that the objects are instantiated. Thus, if we create three employees, we are actually creating three totally distinct instances of an Employee class. Each object contains its own copy of the attributes and methods. For example, consider Figure 6. An employee object called John (John is its identity) has its own copy of all the attributes and methods defined in the Employee class. An employee object called Mary has its own copy of attributes and methods. They both have a separate copy of the DateOfBirth attribute and the getDateOfBirth() method.

Figure 6 - Program spaces.

Physical Copies

Be aware that there is not necessarily a physical copy of each method for each object. Rather, each object points to the same physical code. However, this is an implementation issue left up to the compiler/operating platform. From a conceptual level, you can think of objects as being wholly independent and having their own attributes and methods.

In short, a class is a blueprint for an object. When you instantiate an object, you use a class as the basis for how the object is built. In fact, trying to explain classes and objects is really a chicken-and-egg dilemma. It is difficult to describe a class without using the term object, and to describe an object without using the term class. For example, a specific bike is an object. However, someone had to have the blueprints (that is, the class) to build the bike. In OO software, unlike the chicken-and-egg dilemma, we do know what comes first—the class. An object cannot be instantiated without a class. Thus, many of the concepts in this section are similar to those presented earlier, especially when we talk about attributes and methods.

To explain classes and methods, it's helpful to use an example from the relational database world. In a database table, the definition of the table itself (fields, description, and data types used) would be a class (metadata), and the objects would be the rows of the table (data).

This series focuses on the concepts of OO software, and not on a specific implementation (such as Java, C#, Visual Basic .NET, or C++), but it is often helpful to use code examples to explain some concepts, so Java code fragments are used in this series to help explain some concepts when appropriate. The following sections describe some of the fundamental concepts of classes and how they interact.

Classes Are Object Templates

Classes can be thought of as the templates, or cookie cutters, for objects as seen in Figure 7. A class is used to create an object.

Figure 7 - Class template.

A class can be thought of as a sort of higher-level data type. For example, just as you create an integer or a float:

int x;
float y;

you also can create an object by using a predefined class:

myClass myObject;

Because of the names used in this example, it is obvious that myClass is the class and myObject is the object.

Remember that each object has its own attributes (analogous to fields) and behaviors (analogous to functions or routines). A class defines the attributes and behaviors that all objects created with this class will possess. Classes are pieces of code. Objects instantiated from classes can be distributed individually or as part of a library. Because objects are created from classes, it follows that classes must define the basic building blocks of objects (data, behavior, and messages). In short, you must design a class before you can create an object.

For example, here is a definition of a Person class:

public class Person{
    //Attributes
    private String name;
    private String address;

    //Methods
    public String getName(){
        return name;
    }
    public void setName(String n){
        name = n;
    }
    public String getAddress(){
        return address;
    }
    public void setAddress(String adr){
        address = adr;
    }
}

Attributes

As you already saw, the data of a class is represented by attributes. Each class must define the attributes that will store the state of each object instantiated from that class. In the Person class example in the previous section, the Person class defines attributes for name and address.

Access Designations

When a data type or method is defined as public, other objects can directly access it. When a data type or method is defined as private, only that specific object can access it. Another access modifier, protected, allows access by related objects. You'll learn about this later in the series.

Methods

As you learned earlier, methods implement the required behavior of a class. Every object instantiated from this class has these methods. Methods may implement behaviors that are called from other objects (for example, messages) or provide internal behavior of the class.

Internal behaviors are private methods that are not accessible by other objects. In the Person class, the behaviors are getName(), setName(), getAddress(), and setAddress(). These methods allow other objects to inspect and change the values of the object's attributes. This is common design in OO systems. In all cases, access to attributes within an object should be controlled by the object no other object should directly change an attribute of another.

Messages

Messages are the communication mechanism between objects. For example, when Object A invokes a method of Object B, Object A is sending a message to Object B. Object B's response is defined by its return value. Only the public methods, not the private methods, of an object can be invoked by another object. The following code illustrates this concept:

public class Payroll{
    String name;
    Person p = new Person();
    String = p.setName("Joe");
    ... code
    String = p.getName();
}

In this example (assuming that a Payroll object is instantiated), the Payroll object is sending a message to a Person object, with the purpose of retrieving the name via the getName() method. Again, don't worry too much about the actual code, as we are really interested in the concepts. We will address the code in detail as we progress through the series.

Using UML to Model a Class Diagram

Over the years, many tools and models have been developed to assist in designing classes. The most popular tool today is UML. Although it is beyond the scope of this series to describe UML in fine detail, we will use UML class diagrams to illustrate the classes that we build. In fact, we have already used a class diagram. Figure 8 shows the Person class diagram we discussed earlier.

Figure 8 - The Person class diagram.

Again, notice that the attributes and methods are separated (the attributes on the top, and the methods on the bottom). As we delve more deeply into OO design, these class diagrams will get much more sophisticated and convey much more information on how the different classes interact with each other.

Encapsulation

One of the primary advantages of using objects is that the object need not reveal all its attributes and behaviors. In good OO design (at least what is generally accepted as good), an object should reveal only the interfaces needed to interact with it.

Details not pertinent to the use of the object should be hidden from other objects. This is called encapsulation. For example, an object that calculates the square of a number must provide an interface to obtain the result. However, the internal attributes and algorithms used to calculate the square need not be made available to the requesting object. Robust classes are designed with encapsulation in mind. In the next sections, we cover the concepts of interface and implementation, which are the basis of encapsulation.

Interfaces

As discussed earlier, the interface is the fundamental means of communication between objects. Each class design specifies the interfaces for the proper instantiation and operation of objects. Any behavior that the object provides must be invoked by a message sent using one of the provided interfaces. The interface should completely describe how users of the class interact with the class. In Java, the methods that are part of the interface are designated as public.

Private Data

In accepted OO design, all attributes should be declared as private. Thus, attributes are not part of the interface. Only the public methods are part of the class interface. Declaring an attribute as public breaks the concept of data hiding.

Let's look at the example just mentioned: calculating the square of a number. In this example, the interface would consist of two pieces:

  • How to instantiate a Square object
  • How to send a value to the object and get the square of that value in return

Interfaces do not normally include attributes, only methods. If a user needs access to an attribute, a method is created to return the attribute (a getter). If a user wants the value of an attribute, a method is called that returns the value of the attribute. In this way, the object that contains the attribute controls access to it. This is of vital importance, especially in testing and maintenance.

If you control the access to the attribute, when a problem arises, you do not have to worry about tracking down every piece of code that might have changed the attribute. It can only be changed in one place (the setter).

Interfaces vs. Interfaces

It is important to note that there are interfaces to the classes as well as the methods—don't confuse the two. The interfaces to the classes are the public methods, whereas the interfaces to the methods relate to how you call them.

Implementations

Only the public attributes and methods are considered the interface. The user should not see any part of the implementation interacting with an object solely through class interfaces. In the previous example, for instance the Employee class, only the attributes were hidden. In many cases, there will be methods that also should be hidden and thus not be part of the interface. Continuing the example of the square root from the previous section, the user does not care how the square root is calculated as long as it is the correct answer. Thus, the implementation can change and it will not affect the user's code.

A real-world example of the interface/implementation paradigm

Figure 9 illustrates the interface/implementation paradigm using real-world objects rather than code. The toaster obviously requires electricity. To get this electricity, the cord from the toaster must be plugged into the electrical outlet, which is the interface. All the toaster needs to do to get the required electricity is to use a cord that complies with the electrical outlet specifications; this is the interface between the toaster and the electricity. The fact that the actual implementation is a coal-powered electric plant is not the toaster's concern. In fact, for all the toaster cares, the implementation could be a nuclear power plant or a local power generator. With this model, any appliance can get electricity, as long as it conforms to the interface specification seen in Figure 9.

Figure 9 - Power plant example.

A Java example of the interface/implementation paradigm

Let's explore the Square class further. Assume that you are writing a class that calculates the squares of integers. You must provide a separate interface and implementation. That is, you must provide a way for the user to invoke and obtain the square value. You must also provide the implementation that calculates the square; however, the user should not know anything about the specific implementation. Figure 10 shows one way to do this. Note that in the class diagram, the plus sign (+) designates public and the minus sign (-) designates private.

Thus, you can identify the interface by the methods, prefaced with plus signs.

Figure 10 - The square class.

This class diagram corresponds to the following code:

public class IntSquare {
    // private attribute
    private int squareValue;
    // public interface
    public int getSquare (int value) {
        SquareValue =calculateSquare(value);
        return squareValue;
    }
    // private implementation
    private int calculateSquare (int value) {
        return value*value;
    }
}

Note that the only part of the class that the user has access to is the public method getSquare(), which is the interface. The implementation of the square algorithm is in the method calculateSquare(), which is private. Also notice that the attribute SquareValue is private because users do not need to know that this attribute exists. Therefore, we have hidden the part of the implementation: The object only reveals the interfaces the user needs to interact with it, and details that are not pertinent to the use of the object are hidden from other objects.

If the implementation were to change—say, you wanted to use Java's built-in square function—you would not need to change the interface. The user would get the same functionality, but the implementation would have changed. This is very important when you're writing code that deals with data; for example, you can move data from a file to a database without forcing the user to change any application code.

Conclusion

At this point we are about 2/3 of the way into our discussion on basic object-oriented concepts. Next month, we will conclude the introduction phase of this series and cover the important topics of inheritance, polymorphism, and composition.

About the Author

Matt Weisfeld is a faculty member at Cuyahoga Community College (Tri-C) in Cleveland, Ohio. Matt is a member of the Information Technology department, teaching programming languages such as C++, Java, and C# .NET, as well as various Web technologies. Prior to joining Tri-C, Matt spent 20 years in the information technology industry gaining experience in software development, project management, business development, corporate training, and part-time teaching. Matt holds an MS in computer science and an MBA in project management.

The articles in this series are adapted from The Object-Oriented Thought Process (published by Sams Publishing). Matt has published two other computer books, and more than a dozen articles in magazines and journals such as Dr. Dobb's Journal, The C/C++ Users Journal, Software Development Magazine, Java Report, and the international journal Project Management. Matt has presented at conferences throughout the United States and Canada.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date