Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

lafore_robert_objectoriented_programming_in_c

.pdf
Скачиваний:
51
Добавлен:
27.03.2023
Размер:
8.94 Mб
Скачать

Object-Oriented Software Development

815

Scenarios

As we noted, a use case may consist of several scenarios. So far we’ve described only the main scenario for each use case. This is the scenario where everything works perfectly and the goal is achieved. However, other outcomes are common. As an example of a second scenario in the Add a New Tenant use case, suppose that the user attempts to enter a second tenant into an apartment that is already occupied.

Add a New Tenant, Scenario 2

The program presents the Add Tenant screen, which prompts the user to enter the new tenant’s name and apartment number. However, this apartment number has already been entered in the Tenant List, so it’s rented to someone else. The Add Tenant screen displays an error message to this effect.

Here’s another example of a second scenario, where the user attempts to input a rent payment for a nonexistent tenant.

Input a Rent Payment, Scenario 2

The Rent Input screen prompts the user to enter the name of the tenant, the month the rent is for, and the amount of the rent. The program looks in the Tenant List for the name of the tenant, but does not find it. It displays an error message to the user.

In the interest of simplicity we won’t persue such alternative scenarios, although in a real project each scenario should be developed in as much detail as the major scenarios. Only by doing this can all the programming elements be discovered.

UML Activity Diagrams

The UML activity diagram can be used to model use cases. This kind of diagram shows the flow of control from one activity to another. It’s similar to the flowchart, which has been around since the beginning of programming. However, the activity diagram, like other UML diagrams, is more formally specified and has additional capabilities.

Activities are shown in lozenge-shaped outlines. Lines connecting the activities represent transitions from one activity to another. Branches are shown as diamonds with one incoming and two or more outgoing transitions. As in state diagrams, you can place guards on these transitions to specify which one will be selected. Also as in state diagrams, there is an initial state and an end state, the first represented by a solid circle and the second by a solid circle in a ring.

Figure 16.8 shows the Add a New Tenant use case, including the second scenario just described. The branch depends on whether the apartment number entered by the user is already occupied. If it is, an error message is displayed.

16

O D BJECT EVELOPMENT S

OFTWARE - RIENTEDO

Chapter 16

816

Display

Add Tenant

screen

Get name and

apartment number

 

[Apartment occupied]

[else]

 

Place data

Display

on

error

Tenant List

message

FIGURE 16.8

UML activity diagram.

Activity diagrams can also be used to represent complicated algorithms in program code, just as flowcharts are. They have some capabilities we won’t pursue here, such as representing several concurrent activities.

From Use Cases to Classes

The construction phase of our project begins when we begin to design the program. We’ll start by examining the nouns in the use case descriptions, as mentioned earlier.

Listing the Nouns

Here’s the list of nouns picked out of the use case descriptions:

1.User Interface screen

2.Tenant

3.Tenant Input screen

4.Tenant name

5.Apartment number

6.Tenant row

7.Tenant List

8.Rent payment

9.Rent Input screen

10.Month

11.Rent amount

12.Rent Record

13.Rent row

14.Expense payment

15.Expense Input screen

16.Payee

17.Amount of expense

18.Day

19.Budget category

20.Expense row

21.Expense Record

22.Annual Summary

23.Sum of rents

24.Total expenses by category

25.Balance

Object-Oriented Software Development

817

16

O

D

BJECT

EVELOPMENT S

OFTWARE -

RIENTEDO

Refining the List

For various reasons, many nouns are inappropriate class candidates. Let’s see which ones should be rejected.

We’ve listed the rows in the various records: tenant row, rent row, and expense row. Sometimes these rows make good classes because they are complicated or contain complex data. However, each row in the Tenant Record holds the data for exactly one tenant, and each row in the Expense Record holds the data for exactly one expense. There are already classes for tenant and expense, so we guess that there’s no need for two classes with the same data, and discard the tenant row and expense row classes. The rent row, on the other hand, contains an apartment

Chapter 16

818

number and an array of 12 rents. A rent row doesn’t exist until the first rent of the year has been paid; thereafter, rents are inserted into the existing row. This is a more complicated situation than for tenants and expenses, so we’ll leave rent row as a class. This leaves the rent payment class with no data to hold except the rent amount, so we’ll eliminate this class.

The program can derive the data in the Annual Summary from the Rent Record and the Expense Record, so we probably won’t need to make classes out of the sum of rents, total expenses by category, and balance. These are simply the results of calculations.

This leaves the following classes:

1.User Interface screen

2.Tenant

3.Tenant Input screen

4.Tenant List

5.Rent Input screen

6.Rent Record

7.Rent row

8.Expense payment

9.Expense Input screen

10.Expense Record

11.Annual Summary

Discovering Attributes

Many of the nouns we rejected as classes will be candidates for attributes (member data) in classes. For example, class Tenant will have the attributes Tenant Name and Apartment Number, and class Expense will have the attributes Payee, Month, Day, Amount, and Budget Category. A majority of the attributes can be discovered this way.

From Verbs to Messages

Now let’s look at the use cases to see what light they cast on the messages one class will send to another. Because a message is actually a call to a member function in an object, discovering messages is the same as discovering the member functions of the class receiving the message. As with nouns, not every verb is a candidate for a message. Some relate instead to obtaining information from the user, displaying information or doing other things.

Object-Oriented Software Development

As an example, let’s look at the Display Tenant List use case, with the verbs underlined:

The program displays the Tenant List, each row of which contains an apartment number and the tenant’s name.

By “the program” we really mean the User Interface screen, so “displays” means that the User Interface screen sends a message to—that is, calls a member function of—the Tenant List, telling it to display itself. You can guess that the member function might be named something like display().

The “contains” verb does not correspond to a message; it merely describes the contents of a row in the Tenant List.

Let’s look at a more complicated example: the use case Add a New Tenant:

The program presents the Tenant Input screen, which prompts the user to enter the new tenant’s name and apartment number. It then places this information on a new row in the Tenant List. This list is sorted by apartment number.

The “presents” verb means that the User Interface screen sends a message to the Tenant Input screen telling it to display itself and get data from the user. This message might be a call to a member function in the Tenant Input screen with a name like getTenant().

Both “prompts” and “enter” refer to the Tenant Input screen’s communication with the user. They don’t represent messages in the object-oriented sense. Rather, getTenant() displays prompts and records the user’s responses (the tenant’s name and apartment number).

The verb “places” means that that the Tenant Input screen sends a message to the Tenant List class, probably with a new Tenant object as an argument. The Tenant List object can then insert this new object into its list. This function might have a name like insertTenant().

The “is sorted” verb is not a message or indeed any kind of communication, but a description of the Tenant List.

Figure 16.9 shows the Add a New Tenant use case and its connection to these messages.

When we start to write code, we’ll find that there are some activities that are not mentioned in the use case but are required by the program. For example, the use case does not say anything about the creation of a Tenant object. However, it’s probably clear that the Tenant List holds Tenant objects, and that the Tenant object must be created before being put on the list. The software engineer decides that the getTenant() member function in the Tenant Input screen is an appropriate place to create the Tenant object that will be inserted in the Tenant List.

The other use cases can be similarly analyzed to yield clues to the relationships between classes. Note that at this point we’re still using class names as they appeared in the use cases. When we start to write code we will need to rewrite them as single-word C++ class names.

819

16

O D BJECT EVELOPMENT S

OFTWARE - RIENTEDO

Chapter 16

820

User

getTenant()

Tenant

insertTenant()

Tenant

Interface

 

Input

 

 

 

List

Screen

 

Screen

 

 

 

 

The program presents the Tenant Input screen, which prompts the user to enter the new tenant's name and apartment number. It then places this information on a new row in the Tenant List. This list is sorted by apartment number.

Not used

FIGURE 16.9

Verbs in the Add a New Tenant use case.

Class Diagram

Once we have an idea what classes we will need and how they relate to each other, we can create a class diagram. We’ve seen examples of class diagrams in earlier chapters. Figure 16.10 is the class diagram of the LANDLORD program.

Sequence Diagrams

Before starting to code, we might want to understand in more detail the steps involved in each use case. One way to do this is to generate a UML sequence diagram. A sequence diagram is one of two kinds of UML interaction diagrams. The other is the collaboration diagram. Both show how events unfold over time, but the sequence diagram depicts time in a more graphical way.

In a sequence diagram the vertical axis represents time, starting at the top and flowing downward. Across the top are rectangles containing the names of the objects that will participate in the use case. The action typically starts with the object on the left sending a message to an object on its right. The further to the right they are, the less important (or the more dependent) the objects usually are.

Note that the diagram shows objects, not classes. We’re going to be focusing on sequences of messages, and messages are sent from object to object, not class to class. In UML diagrams, object names are underlined to distinquish them from class names.

Extending downward from each object is a dotted line called the lifeline. This indicates that the object exists at a particular time. If the object is deleted, its lifeline stops at that point.

Object-Oriented Software Development

User

Interface

Tenant

Rent

Expense

Input

Input

Input

Screen

Screen

Screen

Tenant

Rent

Annual

Expense

List

Record

Report

Record

Tenant

Rent

Expense

Row

 

 

FIGURE 16.10

Class diagram of the LANDLORD program.

Sequence Diagram for “Start the Program”

Let’s look at some sequence diagrams for the LANDLORD program. We’ll start with an easy one. Figure 16.11 shows the sequence diagram for the Start the Program use case.

When the program first starts, it defines a class called userInterface to handle the User Interface screen discussed in the use cases. Let’s assume that the program creates a single object of this class called theUserInterface. It’s this object that initiates all the use cases. It will appear on the left in the sequence diagrams. (In these diagrams we condense the use case names into C++ class names.)

When the theUserInterface object starts to run, its first task is to create the three main data structures in the program. These are objects of the classes tenantList, rentRecord, and expenseRecord. As it turns out, the program creates these objects with new, so they are born with no names; only the pointers to them have names. What do we call them? Fortunately, as we saw with object diagrams, the UML allows several ways to write object names. If you don’t know the actual name, you can use a colon and the class name: :tenantList. In the diagrams the underlining and the colon remind you that the name applies to an object, not a class.

821

16

O D BJECT EVELOPMENT S

OFTWARE - RIENTEDO

Chapter 16

822

theUserInterface

new

:tenantList

new

:rentRecord

new

:expenseRecord

FIGURE 16.11

Sequence diagram for the Start the Program use case.

The vertical position of the object rectangles shows the time they were created, starting with the object of class tenantList. All these objects will continue to exist for the life of the program, so their lifelines extend down to the bottom of the diagram. The time dimension is not to scale; it’s intended to show only the relationship of various events.

The horizontal lines represent messages (calls to member functions). The solid arrowhead indicates a normal synchronous function call. (An open arrowhead indicates an asynchronous event.)

The rectangle under theUserInterace is called the activation box (or focus of control). It indicates that its object is active. In a normal procedural program such as LANDLORD, “active” means that a member function of the object is either executing or has called another function that has not yet returned. The three other objects in this diagram are not active because theUserInterface has not yet sent them messages telling them what to do.

Sequence Diagram for “Display Tenant List”

Let’s examine another sequence diagram. This one, shown in Figure 16.12, depicts the Display Tenant List use case.

Object-Oriented Software Development

theUserInterface

:tenantList

:tenant

display()

display()

*[for all tenant objects]

FIGURE 16.12

Sequence diagram for the Display Tenant List use case.

Function returns are represented by dotted lines. Notice how objects are only active (their lifeline has an activity box) when one of their member functions has been called by another object. The message lines can show the name of the member function being called.

Here theUserInterface tells the tenantList object to display itself (by calling its display() function), and the tenantList object in turn tells all the tenant objects it contains to display themselves. The asterisk indicates that this message will be sent repeatedly, and the phrase in brackets, [for all tenant objects], specifies the condition of this repetition. (In the program we’ll actually use cout << instead of a display() function as shown.)

Sequence Diagram for “Add a New Tenant”

As our last example of a sequence diagram let’s look at the Add a New Tenant use case, shown in Figure 16.13. Here we’ve included the landlord as an object, with its own activity box. This allows us to show the interaction between the program and the user.

The user tells the program to add a new tenent. The theUserInterface object creates a new object of class tenantInputScreen. This object gets the tenant’s data from the user, creates a new tenant object, and calls the object of class tenantList to insert the newly created tenant. When it’s done, it deletes the tenantInputScreen object. The large “X” at the end of the tenantInputScreen’s lifeline shows that it is deleted at that point.

823

16

O D BJECT EVELOPMENT S

OFTWARE - RIENTEDO

Chapter 16

824

Landlord

theUserInterface

Add a

:tenantList

tenant

 

 

new

 

:tenantInputScreen

getTenant()

Request data

Supply data

new

:tenant

insertTenant()

delete

FIGURE 16.13

Sequence diagram for the Add a New Tenant use case.

The sequence diagrams we have shown deal only with the main scenario of each use case. There are ways to show alternate scenarios on sequence diagrams, or you can create a new diagram for each scenario.

Lack of space precludes our illustrating sequence diagrams for all the use cases, but you know enough at this point to create them yourself if you want.

Writing the Code

Finally, armed with the use case diagram, the detailed use cases, the class diagram, and the sequence diagrams, you can crank up your compiler and start writing the actual code. This is the second part of the construction phase.