Any discussion of object-oriented software engineering must begin by addressing the term object-oriented. What is an object-oriented viewpo...
Any discussion of object-oriented software engineering must begin by addressing the term object-oriented. What is an object-oriented viewpoint? Why is a method considered to be object-oriented? What is an object? Over the years, there have been many different opinions about the correct answers to these questions. In the discussion that follows, we attempt to synthesize the most common of these.
To understand the object-oriented point of view, consider an example of a real world object—the thing you are sitting in right now—a chair. Chair is a member (the term instance is also used) of a much larger class of objects that we call furniture. A set of generic attributes can be associated with every object in the class furniture. For example, all furniture has a cost, dimensions, weight, location, and color, among many possible attributes. These apply whether we are talking about a table or a chair, a sofa or an armoire. Because chair is a member of furniture, chair inherits all attributes defined for the class. This concept is illustrated schematically in figure.
Once the class has been defined, the attributes can be reused when new instances of the class are created. For example, assume that we were to define a new object called a chable (a cross between a chair and a table) that is a member of the class furniture. Chable inherits all of the attributes of furniture.
We have attempted an anecdotal definition of a class by describing its attributes, but something is missing. Every object in the class furniture can be manipulated in a variety of ways. It can be bought and sold, physically modified (e.g., you can saw off a leg or paint the object purple) or moved from one place to another. Each of these operations (other terms are services or methods) will modify one or more attributes ofthe object. For example, if the attribute location is a composite data item defined as
location = building + floor + room
then an operation named move would modify one or more of the data items (building, floor, or room) that form the attribute location. To do this, move must have "knowledge" of these data items. The operation move could be used for a chair or a table, as long as both are instances of the class furniture. All valid operations (e.g., buy, sell, weigh) for the class furniture are "connected" to the object definition as shown in figure and are inherited by all instances of the class.
The object chair (and all objects in general) encapsulates data (the attribute values that define the chair), operations (the actions that are applied to change the attributes of chair), other objects (composite objects can be defined ), constants (set values), and other related information. Encapsulation means that all of this information is packaged under one name and can be reused as one specification or program component.
Now that we have introduced a few basic concepts, a more formal definition of object-oriented will prove more meaningful. Coad and Yourdon [COA91] define the term this way:
object-oriented = objects + classification + inheritance + communication
Classes and Objects
The fundamental concepts that lead to high-quality design apply equally to systems developed using conventional and object-oriented methods. For this reason, an OO model of computer software must exhibit data and procedural abstractions that lead to effective modularity. A class is an OO concept that encapsulates the data and procedural abstractions required to describe the content and behavior of some real world entity. Taylor {TAY90] uses the notation shown on the right side of figure to describe a class (and objects derived from a class).
The data abstractions (attributes) that describe the class are enclosed by a “wall” of procedural abstractions (called operations, methods, or services) that are capable of manipulating the data in some way. The only way to reach the attributes (and operate on them) is to go through one of the methods that form the wall. Therefore, the class encapsulates data (inside the wall) and the processing that manipulates the data (the methods that make up the wall). This achieves information hiding and reduces the impact of side effects associated with change. Since the methods tend to manipulate a limited number of attributes, they are cohesive; and because communication occurs only through the methods that make up the “wall,” the class tends to be decoupled from other elements of a system. All of these design characteristics lead to highquality software.
Stated another way, a class is a generalized description (e.g., a template, pattern, or blueprint) that describes a collection of similar objects. By definition, all objects that exist within a class inherit its attributes and the operations that are available to manipulate the attributes. A superclass is a collection of classes, and a subclass is a specialized instance of a class.
These definitions imply the existence of a class hierarchy in which the attributes and operations of the superclass are inherited by subclasses that may each add additional “private” attributes and methods. A class hierarchy for the class furniture is illustrated in figure.
Attributes
Attributes are attached to classes and objects, and that they describe the class or object in some way. A discussion of attributes is presented by de Champeaux, Lea, and Favre :
Real life entities are often described with words that indicate stable features. Most physical objects have features such as shape, weight, color, and type of material. People have features including date of birth, parents, name, and eye color. A feature may be seen as a binary relation between a class and a certain domain.The “binary relation” implies that an attribute can take on a value defined by an enumerated
domain. In most cases, a domain is simply a set of specific values. For example, assume that a class automobile has an attribute color. The domain of values for color is {white, black, silver, gray, blue, red, yellow, green}. In more complex situations, the domain can be a set of classes. Continuing the example, the class automobile also has an attribute power train that encompasses the following domain of classes: {16- valve economy option, 16-valve sport option, 24-valve sport option, 32-valve luxury option}. Each of the options noted has a set of specific attributes of its own.
The features (values of the domain) can be augmented by assigning a default value (feature) to an attribute. For example, the power train attribute defaults to 16-valve sport option. It may also be useful to associate a probability with a particular feature by assigned {value, probability} pairs. Consider the color attribute for automobile. In some applications (e.g., manufacturing planning) it might be necessary to assign a probability to each of the colors (white and black are highly probable as automobile colors).
Operations, Methods, and Services
An object encapsulates data (represented as a collection of attributes) and the algorithms that process the data. These algorithms are called operations, methods, or services and can be viewed as modules in a conventional sense.
Each of the operations that is encapsulated by an object provides a representation of one of the behaviors of the object. For example, the operation GetColor for the object automobile will extract the color stored in the color attribute. The implication of the existence of this operation is that the class automobile has been designed to receive a stimulus (we call the stimulus a message) that requests the color of the particular instance of a class. Whenever an object receives a stimulus, it initiates some behavior. This can be as simple as retrieving the color of automobile or as complex as the initiation of a chain of stimuli that are passed among a variety of different objects. In the latter case, consider an example in which the initial stimulus received by object 1 results in the generation of two other stimuli that are sent to object 2 and object 3. Operations encapsulated by the second and third objects act on the stimuli, returning necessary information to the first object. Object 1 then uses the returned information to satisfy the behavior demanded by the initial stimulus.
Messages
Messages are the means by which objects interact. Using the terminology introduced in the preceding section, a message stimulates some behavior to occur in the receiving object. The behavior is accomplished when an operation is executed.
The interaction between messages is illustrated schematically in figure. An operation within a sender object generates a message of the form
Message: [destination, operation, parameters]
where destination defines the receiver object that is stimulated by the message, operation refers to the operation that is to receive the message, and parameters provides information that is required for the operation to be successful.
As an example of message passing within an OO system, consider the objects shown in figure below. Four objects, A, B, C, and D communicate with one another by passing messages. For example, if object B requires processing associated with operation op10 of object D, it would send D a message of the form
message: [D, op10, {data}]
As part of the execution of op10, object D may send a message to object C of the form
message: (C, op08, {data})
Then C finds op08, performs it, and sends an appropriate return value to D. Operation op10 completes and sends a return value to B.
Cox describes the interchange between objects in the following manner:
An object is requested to perform one of its operations by sending it a message telling the object what to do. The receiver [object] responds to the message by first choosing the operation that implements the message name, executing this operation, and then returning control to the caller.Messaging ties an object-oriented system together. Messages provide insight into the behavior of individual objects and the OO system as a whole.
Encapsulation, Inheritance, and Polymorphism
Although the structure and terminology differentiate OO systems from their conventional counterparts, three characteristics of object-oriented systems make them unique. As we have already noted, the OO class and the objects spawned from the class encapsulate data and the operations that work on the data in a single package. This provides a number of important benefits:
• The internal implementation details of data and procedures are hidden from the outside world (information hiding). This reduces the propagation of side effects when changes occur.
• Data structures and the operations that manipulate them are merged in a single named entity—the class. This facilitates component reuse.
• Interfaces among encapsulated objects are simplified. An object that sends a message need not be concerned with the details of internal data structures.
Hence, interfacing is simplified and the system coupling tends to be reduced. Inheritance is one of the key differentiators between conventional and OO systems. A subclass Y inherits all of the attributes and operations associated with its superclass, X. This means that all data structures and algorithms originally designed and implemented for X are immediately available for Y—no further work need be done. Reuse has been accomplished directly.
Any change to the data or operations contained within a superclass is immediately inherited by all subclasses that have inherited from the superclass. Therefore, the class hierarchy becomes a mechanism through which changes (at high levels) can be immediately propagated through a system.
It is important to note that, at each level of the class hierarchy, new attributes and operations may be added to those that have been inherited from higher levels in the hierarchy. In fact, whenever a new class is to be created, the software engineer has a number of options:
• The class can be designed and built from scratch. That is, inheritance is notused.
• The class hierarchy can be searched to determine if a class higher in the hierarchy contains most of the required attributes and operations. The new class inherits from the higher class and additions may then be added, as required.
• The class hierarchy can be restructured so that the required attributes and operations can be inherited by the new class.
• Characteristics of an existing class can be overridden and private versions of attributes or operations are implemented for the new class.
To illustrate how restructuring of the class hierarchy might lead to a desired class, consider the example shown in figure. The class hierarchy illustrated in figure (A) enables us to derive classes X3 and X4 with characteristics 1, 2, 3, 4, 5 and 6 and 1, 2, 3, 4, 5, and 7, respectively. Now, suppose that a new class with only characteristics 1, 2, 3, 4, and 8 is desired. To derive this class, called X2b in the example, the hierarchy may be restructured as shown in figure (B). It is important to note that restructuring the hierarchy can be difficult, and for this reason, overriding is sometimes used.
In essence, overriding occurs when attributes and operations are inherited in the normal manner but are then modified to the specific needs of the new class. As Jacobson notes, when overriding is used “inheritance is not transitive”.
In some cases, it is tempting to inherit some attributes and operations from one class and others from another class. This is called multiple inheritance, and it is controversial. In general, multiple inheritance complicates the class hierarchy and creates potential problems in configuration control . Because multiple inheritance sequences are more difficult to trace, changes to the definition of a class that resides high in the hierarchy may have an unintended impact on classes defined lower in the architecture.
Polymorphism is a characteristic that greatly reduces the effort required to extend an existing OO system. To understand polymorphism, consider a conventional application that must draw four different types of graphs: line graphs, pie charts, histograms, and Kiviat diagrams. Ideally, once data are collected for a particular type of graph, the graph should draw itself. To accomplish this in a conventional application (and maintain module cohesion), it would be necessary to develop drawing modules for each type of graph. Then, within the design for each graph type, control logic similar to the following would have to be embedded:
case of graphtype:
if graphtype = linegraph then DrawLineGraph (data);
if graphtype = piechart then DrawPieChart (data);
if graphtype = piechart then DrawPieChart (data);
if graphtype = histogram then DrawHisto (data);
if graphtype = kiviat then DrawKiviat (data);
end case;
if graphtype = kiviat then DrawKiviat (data);
end case;
Although this design is reasonably straightforward, adding new graph types could be tricky. A new drawing module would have to be created for each graph type and then the control logic would have to be updated for each graph.
To solve this problem, all of the graphs become subclasses of a general class called graph. Using a concept called overloading , each subclass defines an operation called draw. An object can send a draw message to any one of the objects instantiated from any one of the subclasses. The object receiving the message will invoke its own draw operation to create the appropriate graph. Therefore, the design is reduced to
graphtype draw
When a new graph type is to be added to the system, a subclass is created with its own draw operation. But no changes are required within any object that wants a graph drawn because the message graphtype draw remains unchanged. To summarize, polymorphism enables a number of different operations to have the same name. This in turn decouples objects from one another, making each more independent.