This series, The Object-Oriented Thought Process, is intended for someone just learning an object-oriented language and wants to understand the basic concepts before jumping into the code or someone who wants to understand the infrastructure behind an OOP language they are already using. Click here to start at the beginning of the series.
In keeping with the code examples used in the previous articles, Java will be the language used to implement the concepts in code. One of the reasons that I like to use Java is because you can download the Java compiler for personal use at the Sun Microsystems Web site http://java.sun.com/. You can download the J2SE 1.4.2 SDK (software development kit) to compile and execute these applications and I will provide the code listings for all examples in this article. I have the SDK 1.4.0 loaded on my machine. I will also provide figures and the output (when appropriate) for these examples. See the previous articles in this series for detailed descriptions for compiling and running all the code examples.
In the previous article in this series, I covered the topic of object signatures. Despite the fact that object signatures may seem like a fairly basic concept, they are in fact the cornerstones of object-oriented design. The design of the object signatures defines how the objects are used. These signatures can define brand new object services or they can interface to currently existing services. This is where the term of object wrappers comes into play.
In the next few columns, you will explore the concept of object wrappers by considering several interesting technologies. In many ways, object wrapping is simply another example of the paramount object-oriented concept pertaining to separating the interface from the implementation.
Perhaps the most obvious topic to illustrate this concept is that of data handlingsaving program data to external media with the intent of restoring it later. The basic idea is to develop a way to encapsulate various data access technologies and make them transparent to the user. In short, you will create a single interface that will allow the user to save and restore an object, regardless of the technology.
In this article, you will investigate the process of serializing an object so that you can write it out to a file. Next month, you will create wrappers that will allow you to write data from a Java program using JDBC to a database (your first example will be Microsoft Access). In the process of these next few articles, you will focus on the concept of object wrappers. After you cover the topics of object serialization and connecting to a database, you will see the power of object wrappers first hand.
The overriding concept behind object wrappers is that of the separation of the interface from the implementation. By now, you should have realized that this concept is the basis of much of the material in this column. Thus, when designing a class, what the user needs to know and what the user does not need to know are of vital importance. Encapsulation is the means by which nonessential data is hidden from the user.
Consider the example of designing and producing a simple toaster. The toaster, or any appliance for that matter, is simply plugged into an interface, which is an electrical outlet. All appliances can access electricity by complying with and using the correct interface: the electrical outlet. The toaster doesn't need to know about the implementation, or how the electricity is produced. A coal plant or a nuclear plant could produce the electricitythe appliance does not care which, as long as the interface works.
As another example, consider an automobile. The interface between you and the car includes components such as steering wheels, gas pedals, brakes, and ignition switch. For most people, aesthetic issues aside, the main concern when driving a car is that the car starts, accelerates, stops, steers, and so on. The implementation, basically the stuff that you don't see, is of little concern to the average driver. In fact, most people would not even be able to identify certain components, such as the catalytic converters and gaskets. However, any driver would recognize and know how to use the steering wheel because this is a common interface. By installing a standard steering wheel in the car, manufacturers are assured that the people in their target market will be able to use the mechanism.
If, however, a manufacturer decided to install a joystick in place of the steering wheel, most drivers would balk at this, and the automobile might not be a big seller (except for some eclectic people who love bucking the trends). On the other hand, as long as the performance and aesthetics didn't change, the average driver would not notice whether the manufacturer changed the enginepart of the implementationof the automobile.
It must be stressed that the interchangeable engines must be identical in every wayas far as the driver's perceptions go. Replacing a four-cylinder engine with an eight-cylinder engine would change the rules just as changing the current from AC to DC would affect the rules in the power plant example.
The engine is part of the implementation, and the steering wheel is part of the interface. A change in the implementation should have no impact on the driver, whereas a change to the interface might.
Interfaces also relate directly to classes. End users do not normally see any classesthey see the GUI or command line. However, programmers would see the class interfaces. Class reuse means that someone has already written a class. Thus, a programmer who uses a class must know how to use the class. This programmer will combine many classes to create a system. The programmer is the one who needs to understand the interfaces of a class. Therefore, when I talk about users in this article, I mean designers and developersnot end users. And when I talk about interfaces, I are talking about class interfaces, not GUIs.
To encapsulate data, classes are designed in two partsthe interface and the implementation.
The interface is the services that are presented to an end user. In the best case, only the services that the end user needs are presented. Of course, which services the user needs may be a matter of opinion. If you put 10 people in a room and ask each of them to do an independent design, you might receive 10 totally different designs. There is nothing wrong with this. However, as a rule of thumb, the interface to a class should contain only what the user needs to know. In the toaster example, the user only needs to know that the toaster must be plugged into the interfacewhich in this case is the electrical outlet.
Perhaps the most important issue when designing a class is identifying the audience, or users, of the class.
The implementation details of the interface services are hidden from the user and can be changed as long as the interface remains the same. Recall that in the toaster example, although the interface is always the electric outlet, the implementation could change from a coal power plant to a nuclear power plant without affecting the toaster. There is one very important caveat to be made here: The coal or nuclear plant must also conform to the interface specification. If the coal plant produces AC power, but the nuclear plant produces DC power, there is a problem. The bottom line is that both the user and the implementation must conform to the interface specification.
Using Wrappers to Hide the Implementation
How does the concept of interface/implementation relate to the discussion on serialization and connecting to a database? The basic idea is that you as a user of a class should be able to write an object to a persistence data storage device without knowing what the implementation of the device is. This holds for specific implementations like whether or not you use an MS Access database or an Oracle database, and so on. It also holds true for the actual means of storage, like whether or not you use serialization or connect to a database. In short, all you should have to do is write the object; the underlying implementation should be hidden. In this way, you can change the implementation without affecting the user code. You will explore this concept in great detail after you cover the specific technologies. As stated earlier, this article describes object serialization; the next will cover connecting to a database using JDBC. Then, you will build the appropriate wrappers to allow you to seamlessly use one or both of these technologies. Thus, you can see how to write an object to a file.
Serializing an Object
No matter what type of business application that you create, saving the data to a storage device most likely will be part of the mix. In fact, one of my favorite lines when it comes to software development is "it's all about the data." In short, no matter what hardware, operating system, applications software, and so forth, is used when creating a software application, the data may well be the reason for creating the system in the first place.
Persistent Objects Basics
Recall that when an object is instantiated by an application, it lives only as long as the application itself. Thus, if you instantiate an Employee object that contains attributes such as name, ss#, and the like, that Employee object will cease to exist when the application terminates. Figure 1 illustrates the traditional object life cycle.
Figure 1: The Object Life Cycle.
When the Employee object is instantiated and initialized, it has a specific state. Remember that the state of an object is defined by the value of is attributes. If you want to maintain the state of the Employee object, you must take some sort of action to save the state of this object beyond the life of the application. The concept of saving the state of an object so that it can be used later is called persistence. Thus, you use the term persistent object to define an object that can be restored and used independent of the application. Figure 2 illustrates the traditional object life cycle with persistence.
Figure 2: Object Life Cycle with Persistence.
There are many ways to save the state of an object. Some of these are as follows:
- Save to a flat file
- Save to a relational database
- Save to an object database