[Table of Contents] [Previous] [Next] [Object Central Home]
The Essence of Object-Oriented Programming
Copyright © 1998, Bruce E. Wampler
4.1 The Coad-Yourdon Methodology
4.2 The Problem Specification
4.3 Object Discovery
4.4 Choosing Names
4.5 Class hierarchies
4.6 Object relationships
4.7 Object attributes
4.8 Object behaviors
4.9 Object methods
4.10 Chapter Summary
The main goal of object-oriented analysis is to examine the problem from the viewpoint of the customer1 and to construct a formal and detailed model of the customer's requirements. This process consists mainly of discovering the objects in the system and determining how the objects behave and relate to other objects. The analysis phase should be completely independent of the ultimate target environment, including the particular computer or programming language.
The main goal of the object-oriented design phase is to take the objects found in the analysis and design a coherent software architecture model that uses the objects in a system. The design will account for things such as object libraries available for reuse, the user interface, and the target environment.
The split between object-oriented analysis and design is less clear than it is with other methodologies, although there is still a distinction. With older structured methodologies, the analysis and design phases were often very different and used different tools and notations. With object-orientation, the analysis and design phases are much more similar, to the point that they can often use the same notation and tools.
The unification of OOA and OOD is a really big advantage of the object-oriented paradigm. With older methodologies, the fact of the matter is that it is often tempting to skip the analysis and get on with the design, especially for smaller software systems. With object orientation, the analysis phase is really quite useful. You will end up with a clear identification of the objects that belong in the system, and a good picture of how they fit together. There really is no wasted effort. Everything carries over directly to the design, and then to the coding.
When building an OO system, the analysis, design, and implementation phases can overlap, and the process is often quite iterative. Part of this arises from the fact that objects and related groups of objects can be defined independently from other objects in the system. Given this overlapping and iterative process, prototyping systems are especially useful for OO development
Nevertheless, for a given object or group of objects, the analysis, design, and implementation will proceed sequentially. The process will still likely be iterative, with the results of prototypes or partial implementations feeding back to refinements in the analysis or design. For small projects, or parts of large projects, it will often be difficult to determine just what stage the development is in.
It is still very important for the development of a successful system to follow all the steps of analysis, design, and implementation. During the analysis, the problem should be treated abstractly, with minimal considerations of the final implementation. The design stage should concentrate on the larger architectural issues, and how the system will be organized and put together. If the analysis and design produce a solid, well-structured system, the final implementation will be far easier to code and maintain.
In this book, we will use a slightly simplified version of the Coad-Yourdon methodology of object-oriented analysis and design. The Coad-Yourdon methodology is one of the easiest to learn and simplest to use, and includes the essentials needed for effective OOA and OOD.
The basic analysis and design steps of the Coad-Yourdon methodology include the discovery and determination of:
The basic notation of the the Coad-Yourdon methodology is easy to learn and simple to use. A slightly simplified version2 of the basic graphical elements of the notation are shown in Figure 4.1.
An object is shown in a rectangular box. A single border indicates a generalized base class that will not have any instances, while a double border indicates that the named class can have instances. Generalization/specialization (inheritance, or is-a) relationships are shown as connecting lines with half circle symbols. Whole/part (aggregation, or has-a) relationships are shown by connecting lines with triangle symbols3.
The ``1,N'' notation at the top of the aggregation triangle indicates that the upper object can contain from 1 to N instances of the lower object. The lower ``1,N'' indicates the lower object can be a part of 1 to N objects. The values can be changed to reflect reality. Thus, it is common to have ``1,N'' at the top, indicating that an object may contain many instances of the lower object, and just a ``1'' for the lower value, indication that an object is a part of exactly one of the upper objects. Zero is a valid value for the first number, too.
Relationships among objects are shown by simple lines with text labels describing the relationships. Relationships can also use the ``1,N'' notation to indicate quantitative aspects of the relationship. Relationships can also be described with a label next to the connecting line.
When discussing a design at a high level, the attributes and methods boxes are often left blank. This allows you to focus on the objects, the class hierarchies, and the relationships among objects.
The first step of the analysis process is to examine the written description of the problem. If there isn't one (there isn't?), then the first step should be to create a written specification with sessions with the customer. For large projects, producing an adequate specification of the problem can be a major undertaking, requiring months of effort. Even the smallest project should have a written description of the problem that includes what the project will be required to produce. This applies to small projects you might be doing for yourself. You should take the time to produce at least a basic description of the problem.
In order to have a concrete example to use when discussing the issues of analysis and design, we will present a short specification of a simple problem. Rather that describing a whole problem, we will use as an example a utility that provides service to a larger project, yet can stand alone for discussion. Part III of this book will include a much more detailed case study of a larger object-oriented project.
Imagine that the customer is building a software system to analyze English text. The analysis will include checking for grammar and spelling errors. Since the analysis needs to work with sentences, a utility package to handle manipulation of sentences is required. This sentence package will serve as our example.
The utility must be able to build and manipulate the elements of a sentence. The outside program will determine what makes up a sentence, and provide our utility with correctly spelled words and punctuation marks that make up the sentence. Our utility will need to provide access to each part of the sentence. It will have the responsibility of keeping track of certain information about each sentence piece, including part of speech or punctuation type, and length. The utility will also allow the sentence to be modified by adding or removing parts.
For any OO methodology, the single most crucial aspect of the analysis and design is the determination of which objects and classes to include in the model. Objects are at the core of any OO system, and it requires a clear vision of just which objects belong in the system to produce a good OOA model.
Thus, the first step of the analysis process is to use the written problem specification to determine likely candidates for objects used in the system. As the model of the system is refined, objects found in the analysis phase will be carried over directly to the design phase to create the architecture of the system, and most will eventually end up implemented in real code.
A good starting point for object discovery is to find the nouns and verbs in the project description as described in Section 4.3.1. Once you have some candidate objects, you can examine the problem in more detail.
Depending on the nature of the problem, there are three viewpoints you can take to to examine objects. You can usually look at the problem with a data oriented focus, a behavioral focus, or possibly a functional focus. One perspective is likely to be a better fit for a given problem, and most OO projects tend to be either data driven or event (behavior) driven.
When first starting out with object discovery, try to discover as many candidate objects as possible. Finding too may objects can be a problem, but you will be able to shorten and refine the list of candidate objects later. It is probably better to start with too many objects than with too few. And of course, just because you are using an object-oriented approach, it doesn't mean the customer knows or understands exactly what they want or need, or that their specification is really accurate or complete.
A good first pass at object discovery is to use the textual problem description to pick out the nouns and verbs. The nouns represent candidate objects, and the verbs represent candidate operations or methods that go with the objects.
Coming up with a definitive candidate list of objects is not a trivial task, and no two analysts or designers are likely to come up with the same list. Picking objects from the nouns and verbs in the description is a simple and direct way to get started. Often, once a few objects have been identified, it will be easier to find other candidate objects using the perspectives described in the following sections.
An examination of the sentence utility specifications gives the following list of nouns:
Each of these nouns is a candidate object. The following verbs are found in the description:
These verbs represent candidate methods.
For many applications, manipulating data is the major purpose of the system. Most applications use data and data structures in some form or another. Thus, using a data oriented perspective of the system is often the primary way to discover objects.
Coad-Yourdon suggest the following places to look for objects using the data perspective4:
An examination of the sentence problem space really does not reveal any new candidate objects. Examining the interaction with the outside world again confirms the list of candidate methods we already have. There really aren't any physical devices or events to remember. Considering physical locations suggests that there might be some relationship between the sentence and its location in the original source document that needs to be tracked. Looking at organizational units suggests that we might want to keep track of phrases contained in a sentence as well as the words and punctuation marks. We will add document location and phrases to our list of candidates.
Examining objects from a functional perspective involves identifying the responsibilities of objects. What function does an object perform, what are its responsibilities to the rest of the system? One way to look for function is to concentrate on the verbs in the problem statement. Sometimes discovering a function can lead to the discovery of several objects required to carry out that function.
We've already examined the verbs in our sentence problem. There aren't any additional responsibilities suggested by examining the responsibilities of the sentence utility.
Examining the behavior of a candidate object can lead to the discovery of new objects, or the validation of that candidate object. The purpose is to examine how objects communicate and with whom, and how do the objects respond to messages, interrupts, or other forms of communication.
It can be very useful to do some behavioral role playing or personification with a candidate object. Try to personify or imagine yourself as the object. Ask yourself questions. Who do I interact with? How do I respond to a message from some other object? What is my job? What do I do? What do I contribute to the system? What do I need to remember?
The answers to these questions can provide clues to whether the candidate object is a good object or not, and possibly other classes needed to support its behavior. They can also lead to discovering methods that need to go with an object.
Since a sentence seems to be the main object, we can ask ourselves, ``As a sentence, what who do I interact with? What is my job? What do I need to remember?'' The answers reveal that we interact with the rest of the system, and that it will provide us with the pieces that make up a sentence. Our job is to remember these pieces, and provide access to them. Our purpose is mainly storage and retrieval, and does not include much active manipulation of the pieces of a sentence. The result of this analysis is that it will be important our sentence utility to provide easy access to the pieces of the sentence for the outside world.
Notice how this process overlaps other analysis phases. At this point, we're ready to discuss methods, but we haven't really determined which objects belong in the system yet. We also have to remember we are at the analysis stage, and resist the temptation to skip ahead to the design and not yet start to think about specific ways of representing words or providing efficient access. We should still avoid thinking about specific implementation issues.
Once you have a list of candidate objects, it is important to examine each object for validity. It is easy to get too many objects. You should remember inheritance, and look for the possibility of generalizing some of your objects into a higher level class. Are all the objects you have really necessary, or could they be combined into a common class? In general, smaller is better.
There are some objective criteria that can be applied to objects to evaluate their ``goodness.'' Coad and Yourdon suggest the following6:
We can now evaluate our sentence object candidate list using the above guidelines.
What we have is a candidate for a new ``sentence element'' class, with words and punctuation marks as subclasses. We will put the shared attributes in the sentence element class, and identify the differences in the subclasses. Applying the is-a test (a word is a sentence element, and a punctuation mark is a sentence element) confirms we have identified a good case for inheritance.
This is an example of discovering something in that analysis that would require additional input from the customer. In this case, we might determine that document location was important, and that it is an attribute of each sentence element. Thus, we will add it as an attribute to the sentence element class.
We will also use the attributes we've identified so far as we fill in the details of the objects later.
Choosing good names for things is an important part of the analysis process. In an object-oriented design, classes, objects, attributes, methods, and source code files are among the things that need names. The names you select should reflect the semantics of the item. Just a good writer uses the English language carefully and effectively, a good programmer will carefully and effectively choose names.
Consider the following suggestions for picking names.
Sentence seems like a good name for the sentence class. A specific Sentence object might then be called theSentence.
Sentence element seems a bit awkward. A common computer science term for a piece of text is a token, so we will call the general sentence element class Token. Word and Punctuation are good names for the word and punctuation mark subclasses.
Figure 4.2 shows the objects we've identified. The figure shows only the names of the objects.
Note that there will never be an instance of a Token object since it is really an abstract base class for Word and Punctuation objects. You would likely have a list of Token objects which you might name TokenList. You also might have a pointer to a Token object, which in fact would be either a Word or Punctuation object. As you manipulate a Token object through the pointer, polymorphism will ensure the appropriate methods for Word or Punctuation objects are used, even though the pointer is to a general Token.
Once you have a good list of candidate objects, the next phase of OOA is to organize the basic objects into hierarchies. Object discovery and hierarchy discovery often overlap. While examining a problem for objects, it will often be quite apparent that some objects naturally form a hierarchy. Other hierarchies will not be as obvious, and must be discovered explicitly.
The main goal of this phase is to identify the hierarchies that will take advantage of the OO paradigm. The two major forms of hierarchies are generalization/specialization, or inheritance, and whole/part, or aggregation.
The concept of inheritance with superclasses and subclasses is natural for many problems. Inheritance implies that a subclass will inherit the properties of the superclass, as well as adding new properties of its own.
Whole/part discovery often takes place after the initial object discovery. Sometimes the whole/part relationship is obvious, but sometimes it is helpful to look for whole/part hierarchies explicitly.
The hierarchy of the sentence example is not very complex. The Sentence class has Token objects. The Word and Punctuation classes are subclasses of the Token class. This hierarchy is shown in Figure 4.3.
Hierarchies reveal how related or similar objects fit together. Objects also have relationships with other objects that reflect how the objects interact in ways other than inheritance and aggregation. For example, a relationship might indicate that one object uses the services or generates an instance of another class. A ``Customer'' object might generate an ``Order'' object. The third phase of OOA is to identify relationships.
Figure 4.4 shows some of the common relationships that have been identified by Coad and Yourdon. These relationships can serve as guidelines for discovering the relationships in your design.
Relationships are usually bidirectional, although behavior might result in an implementation with one-way messages. The relationships represent the model, not the implementation.
The sentence utility does not have any internal relationships. There would be a relationship between a sentence and the part of the outside program than scans the source document for sentences and interacts with the sentence class. Thus, the scanner object would generate instances of sentence objects.
So far, we have discussed finding objects and discovering object hierarchies and relationships. OOA must also examine object attributes. An object's attributes describe its meaning - what data it holds, what state it is in, what connections it has to other objects.
The task of discovering attributes in an OO system is often not much different than it is using other methodologies. You must study the system and try to learn as much as possible about the classes and objects you've identified.
Objects generally have two kinds of attributes, public and private7. Public attributes are available to the world, while private attributes are used internally. Generally, during OOA, you are concerned primarily with the public attributes.
Imagining yourself to be the object works for attribute identification, just as it does for object identification as discussed earlier. Become the object in your mind and ask questions such as:
As we noted earlier, the attributes discovered in the OOA phase are public attributes. At this point we assume that other objects will be able to access these attributes. The attributes reflect the model, and not the implementation design. In practice, you almost never make an attribute directly available to the outside world. Instead, you provide selector and modifier methods to access the attributes. Coad-Yourdon notation doesn't really provide a mechanism to distinguish public and private attributes (or methods, for that matter). The notation generally assumes that both the attributes and methods included in the object box diagram are available to other objects, and that selector and modifier methods are not included in the description.
We've already discovered some of the attributes of our sentence class. For example, we know we need to remember the length of a token, and where the token is in the original document, and so on. Let's discuss the attributes of each required for each class we've identified.
The Sentence class really doesn't need many attributes. Basically, it needs to keep track of the tokens in the sentence. In the end, this will probably be a list, but that is a detail that belongs in the design. The hierarchy diagram with the aggregation connection between Sentence and Token is enough at this time. It is likely that the number of tokens contained in the sentence will be useful information, so there will be a token count attribute. While the sentence class does not have many attributes, it does have several important methods we will discuss later.
The Token class has several attributes. The document location discussed earlier is one. Another attribute that covers both Word and Punctuation objects is the length of the token. It might seem that the string representation of the token should be included in Token, but if we push that down to the subclasses, we can have different representations for words and punctuation marks. The Token class probably does need a type attribute so the outside program can make decisions depending if the token is a word or a punctuation mark. An alternative to a type attribute would be to combine part of speech attributes and punctuation attributes into a single attribute.
Now that we are down to the lowest level, we can determine the attributes needed for Word and Punctuation objects. These attributes should be somewhat different since we decided earlier the objects were different.
A Word object will have some kind of string representation of the word, and a representation of the part of speech. A Punctuation object will have a representation of the punctuation mark, and a representation of special properties of that kind of punctuation mark.
Hierarchies, relationships, and attributes represent the static characteristics of classes and objects. But objects also are dynamic, and have behavior. A major part of an object's behavior is how it responds to messages. Behavior of objects can be modeled using state-transition diagrams.
It may not be necessary to produce a state-transition diagram for every class of objects in every system, but they are especially useful for system with time-dependent behavior. Such real-time systems typically deal with high-speed external data, and need to respond appropriately.
The sentence class really does not need to be modeled with a state diagram.
Methods8 are the services or operations a class defines to implement the behavior of member objects. Methods are how an object interacts with other objects in the system. Discovery of the methods used to implement object behavior is also an important OOA activity.
Just as a class will have both private and public attributes, it will have private and public methods. During the analysis phase, method discovery will concentrate on public methods, and not those private methods used internally by a class.
There are several kinds of methods commonly associated with a class. Given the importance of messages in OO systems, the methods that respond to messages may be the most obvious to examine first. They define how other objects will interact with members of the class, and how they will respond.
Other objects often need to set or get the attributes of an object. Selector and modifier methods are used to provide this access. During OOA, these are often implicitly assumed to be available for each public attribute.
While the relationships among classes and objects can be described statically, in practice the specific relationship between instances of objects is established dynamically as the objects execute. Classes need to provide methods used to build these connections.
Finally, each class will have several methods associated with overhead chores such as creating a new instance, deleting an instance, or making a copy of an instance9. Coad-Yourdon call these ``implicit'' methods, and usually do not show them on the OO diagrams.
You can use the above classifications to help you discover the methods that belong with a given class. Inheritance also makes a difference when describing methods. You will want to move methods that describe shared behaviors as high as possible in the hierarchy. You will also need to identify methods that will be overridden by subclasses.
We will now consider the methods that are needed for the sentence package. The Sentence class represents the main interface to the outside world. It will need methods to add, insert, and delete new tokens, as well as iterator methods to access each token found in the sentence. These methods are listed in the Sentence object in Figure 4.5.
Once the application has obtained a token via one of the Sentence access methods, it will need to manipulate each token directly. Since we've designed for different attributes for Word and Punctuation objects, the Token class needs to provide methods to change a token, and retrieve its representation and attributes. These methods are listed in Figure?4.5.
Other than the implicit selectors and modifiers, the Word and Punctuation classes don't seem to need any additional methods. The final classes with attributes and methods for the sentence package are shown in Figure 4.5.
Visits:
1 We use the term customer to refer to whoever is requesting the software. This may or may not be the end-user.
2 The real notation uses boxes with rounded corners and thick borders. We will use square corners and thin lines.
3 Hint: It is sometimes hard to remember which shape is which. A triangle looks like a capital letter A as in Aggregation. The half circle shape is then inheritance.
4 Coad and Yourdon, Object-Oriented Analysis.
5 This is really just a broader examination of the problem than the simpler noun/verb discovery technique described in the previous section.
6 Coad and Yourdon, Object-Oriented Analysis
7 C++ adds protected attributes which can be accessed by subclasses.
8 The term method seems to be the most widely used, but the equivalent terms service or operation are some times also used. Methods are implemented as member functions in C++.
9 E.g., the constructor, the destructor, and the copy constructor in C++.