(Optional) Software Engineering Case Study: Starting to Program the Classes of the ATM System

(Optional) Software Engineering Case Study Starting to Program the Classes of the ATM System

In the "Software Engineering Case Study" sections in Chapters 17, we introduced the fundamentals of object orientation and developed an object-oriented design for our ATM system. Earlier in this chapter, we discussed many of the details of programming with C++ classes. We now begin implementing our object-oriented design in C++. At the end of this section, we show how to convert class diagrams to C++ header files. In the final "Software Engineering Case Study" section (Section 13.10), we modify the header files to incorporate the object-oriented concept of inheritance. We present the full C++ code implementation in Appendix G.

Visibility

We now apply access specifiers to the members of our classes. In Chapter 3, we introduced access specifiers public and private. Access specifiers determine the visibility or accessibility of an object's attributes and operations to other objects. Before we can begin implementing our design, we must consider which attributes and operations of our classes should be public and which should be private.

In Chapter 3, we observed that data members normally should be private and that member functions invoked by clients of a given class should be public. Member functions that are called only by other member functions of the class as "utility functions," however, normally should be private. The UML employs visibility markers for modeling the visibility of attributes and operations. Public visibility is indicated by placing a plus sign (+) before an operation or an attribute; a minus sign () indicates private visibility. Figure 9.20 shows our updated class diagram with visibility markers included. [Note: We do not include any operation parameters in Fig. 9.20. This is perfectly normal. Adding visibility markers does not affect the parameters already modeled in the class diagrams of Figs. 6.226.25.]

Figure 9.20. Class diagram with visibility markers.

(This item is displayed on page 510 in the print version)

 

Navigability

Before we begin implementing our design in C++, we introduce an additional UML notation. The class diagram in Fig. 9.21 further refines the relationships among classes in the ATM system by adding navigability arrows to the association lines. Navigability arrows (represented as arrows with stick arrowheads in the class diagram) indicate in which direction an association can be traversed and are based on the collaborations modeled in communication and sequence diagrams (see Section 7.12). When implementing a system designed using the UML, programmers use navigability arrows to help determine which objects need references or pointers to other objects. For example, the navigability arrow pointing from class ATM to class BankDatabase indicates that we can navigate from the former to the latter, thereby enabling the ATM to invoke the BankDatabase's operations. However, since Fig. 9.21 does not contain a navigability arrow pointing from class BankDatabase to class ATM, the BankDatabase cannot access the ATM's operations. Note that associations in a class diagram that have navigability arrows at both ends or do not have navigability arrows at all indicate bidirectional navigabilitynavigation can proceed in either direction across the association.


Figure 9.21. Class diagram with navigability arrows.

(This item is displayed on page 511 in the print version)

Like the class diagram of Fig. 3.23 the class diagram of Fig. 9.21 omits classes BalanceInquiry and Deposit to keep the diagram simple. The navigability of the associations in which these classes participate closely parallels the navigability of class Withdrawal's associations. Recall from Section 3.11 that BalanceInquiry has an association with class Screen. We can navigate from class BalanceInquiry to class Screen along this association, but we cannot navigate from class Screen to class BalanceInquiry. Thus, if we were to model class BalanceInquiry in Fig. 9.21, we would place a navigability arrow at class Screen's end of this association. Also recall that class Deposit associates with classes Screen, Keypad and DepositSlot. We can navigate from class Deposit to each of these classes, but not vice versa. We therefore would place navigability arrows at the Screen, Keypad and DepositSlot ends of these associations. [Note: We model these additional classes and associations in our final class diagram in Section 13.10, after we have simplified the structure of our system by incorporating the object-oriented concept of inheritance.]


Implementing the ATM System from Its UML Design

We are now ready to begin implementing the ATM system. We first convert the classes in the diagrams of Fig. 9.20 and Fig. 9.21 into C++ header files. This code will represent the "skeleton" of the system. In Chapter 13, we modify the header files to incorporate the object-oriented concept of inheritance. In Appendix G, ATM Case Study Code, we present the complete working C++ code for our model.

As an example, we begin to develop the header file for class Withdrawal from our design of class Withdrawal in Fig. 9.20. We use this figure to determine the attributes and operations of the class. We use the UML model in Fig. 9.21 to determine the associations among classes. We follow the following five guidelines for each class:


  1. Use the name located in the first compartment of a class in a class diagram to define the class in a header file (Fig. 9.22). Use #ifndef, #define and #endif preprocessor directives to prevent the header file from being included more than once in a program.

    Figure 9.22. Definition of class Withdrawal enclosed in preprocessor wrappers.

    (This item is displayed on page 512 in the print version)

     1 // Fig. 9.22: Withdrawal.h
     2 // Definition of class Withdrawal that represents a withdrawal transaction
     3 #ifndef WITHDRAWAL_H
     4 #define WITHDRAWAL_H
     5
     6 class Withdrawal
     7 {
     8 }; // end class Withdrawal
     9
    10 #endif // WITHDRAWAL_H
    
  2. Use the attributes located in the class's second compartment to declare the data members. For example, the private attributes accountNumber and amount of class Withdrawal yield the code in Fig. 9.23.

    Figure 9.23. Adding attributes to the Withdrawal class header file.

    (This item is displayed on page 513 in the print version)

     1 // Fig. 9.23: Withdrawal.h
     2 // Definition of class Withdrawal that represents a withdrawal transaction
     3 #ifndef WITHDRAWAL_H
     4 #define WITHDRAWAL_H
     5
     6 class Withdrawal
     7 {
     8 private:
     9 // attributes 
    10 int accountNumber; // account to withdraw funds from
    11 double amount; // amount to withdraw 
    12 }; // end class Withdrawal
    13
    14 #endif // WITHDRAWAL_H
    
  3. Use the associations described in the class diagram to declare references (or pointers, where appropriate) to other objects. For example, according to Fig. 9.21, Withdrawal can access one object of class Screen, one object of class Keypad, one object of class CashDispenser and one object of class BankDatabase. Class Withdrawal must maintain handles on these objects to send messages to them, so lines 1922 of Fig. 9.24 declare four references as private data members. In the implementation of class Withdrawal in Appendix G, a constructor initializes these data members with references to actual objects. Note that lines 69 #include the header files containing the definitions of classes Screen, Keypad, CashDispenser and BankDatabase so that we can declare references to objects of these classes in lines 1922.


    Figure 9.24. Declaring references to objects associated with class Withdrawal.

    (This item is displayed on page 513 in the print version)

     1 // Fig. 9.24: Withdrawal.h
     2 // Definition of class Withdrawal that represents a withdrawal transaction
     3 #ifndef WITHDRAWAL_H
     4 #define WITHDRAWAL_H
     5
     6 #include "Screen.h" // include definition of class Screen 
     7 #include "Keypad.h" // include definition of class Keypad 
     8 #include "CashDispenser.h" // include definition of class CashDispenser
     9 #include "BankDatabase.h" // include definition of class BankDatabase 
    10
    11 class Withdrawal
    12 {
    13 private:
    14 // attributes
    15 int accountNumber; // account to withdraw funds from
    16 double amount; // amount to withdraw
    17
    18 // references to associated objects 
    19 Screen &screen; // reference to ATM's screen 
    20 Keypad &keypad; // reference to ATM's keypad 
    21 CashDispenser &cashDispenser; // reference to ATM's cash dispenser 
    22 BankDatabase &bankDatabase; // reference to the account info database
    23 }; // end class Withdrawal
    24
    25 #endif // WITHDRAWAL_H
    
  4. It turns out that including the header files for classes Screen, Keypad, CashDispenser and BankDatabase in Fig. 9.24 does more than is necessary. Class Withdrawal contains references to objects of these classesit does not contain actual objectsand the amount of information required by the compiler to create a reference differs from that which is required to create an object. Recall that creating an object requires that you provide the compiler with a definition of the class that introduces the name of the class as a new user-defined type and indicates the data members that determine how much memory is required to store the object. Declaring a reference (or pointer) to an object, however, requires only that the compiler knows that the object's class existsit does not need to know the size of the object. Any reference (or pointer), regardless of the class of the object to which it refers, contains only the memory address of the actual object. The amount of memory required to store an address is a physical characteristic of the computer's hardware. The compiler thus knows the size of any reference (or pointer). As a result, including a class's full header file when declaring only a reference to an object of that class is unnecessarywe need to introduce the name of the class, but we do not need to provide the data layout of the object, because the compiler already knows the size of all references. C++ provides a statement called a forward declaration that signifies that a header file contains references or pointers to a class, but that the class definition lies outside the header file. We can replace the #includes in the Withdrawal class definition of Fig. 9.24 with forward declarations of classes Screen, Keypad, CashDispenser and BankDatabase (lines 69 in Fig. 9.25). Rather than #include the entire header file for each of these classes, we place only a forward declaration of each class in the header file for class Withdrawal. Note that if class Withdrawal contained actual objects instead of references (i.e., if the ampersands in lines 1922 were omitted), then we would indeed need to #include the full header files.

    Figure 9.25. Using forward declarations in place of #include directives.

    (This item is displayed on page 514 in the print version)

     1 // Fig. 9.25: Withdrawal.h
     2 // Definition of class Withdrawal that represents a withdrawal transaction
     3 #ifndef WITHDRAWAL_H
     4 #define WITHDRAWAL_H
     5
     6 class Screen; // forward declaration of class Screen 
     7 class Keypad; // forward declaration of class Keypad 
     8 class CashDispenser; // forward declaration of class CashDispenser
     9 class BankDatabase; // forward declaration of class BankDatabase 
    10
    11 class Withdrawal
    12 {
    13 private:
    14 // attributes
    15 int accountNumber; // account to withdraw funds from
    16 double amount; // amount to withdraw
    17
    18 // references to associated objects
    19 Screen &screen; // reference to ATM's screen
    20 Keypad &keypad; // reference to ATM's keypad
    21 CashDispenser &cashDispenser; // reference to ATM's cash dispenser
    22 BankDatabase &bankDatabase; // reference to the account info database
    23 }; // end class Withdrawal
    24
    25 #endif // WITHDRAWAL_H
    

    Note that using a forward declaration (where possible) instead of including a full header file helps avoid a preprocessor problem called a circular include. This problem occurs when the header file for a class A #includes the header file for a class B and vice versa. Some preprocessors are not be able to resolve such #include directives, causing a compilation error. If class A, for example, uses only a reference to an object of class B, then the #include in class A's header file can be replaced by a forward declaration of class B to prevent the circular include.

  5. Use the operations located in the third compartment of Fig. 9.20 to write the function prototypes of the class's member functions. If we have not yet specified a return type for an operation, we declare the member function with return type void. Refer to the class diagrams of Figs. 6.226.25 to declare any necessary parameters. For example, adding the public operation execute in class Withdrawal, which has an empty parameter list, yields the prototype in line 15 of Fig. 9.26. [Note: We code the definitions of member functions in .cpp files when we implement the complete ATM system in Appendix G.]

Figure 9.26. Adding operations to the Withdrawal class header file.

(This item is displayed on pages 514 - 515 in the print version)

 1 // Fig. 9.26: Withdrawal.h
 2 // Definition of class Withdrawal that represents a withdrawal transaction
 3 #ifndef WITHDRAWAL_H
 4 #define WITHDRAWAL_H
 5
 6 class Screen; // forward declaration of class Screen
 7 class Keypad; // forward declaration of class Keypad
 8 class CashDispenser; // forward declaration of class CashDispenser
 9 class BankDatabase; // forward declaration of class BankDatabase
10
11 class Withdrawal
12 {
13 public:
14 // operations
15 void execute(); // perform the transaction
16 private:
17 // attributes
18 int accountNumber; // account to withdraw funds from
19 double amount; // amount to withdraw
20
21 // references to associated objects
22 Screen &screen; // reference to ATM's screen
23 Keypad &keypad; // reference to ATM's keypad
24 CashDispenser &cashDispenser; // reference to ATM's cash dispenser
25 BankDatabase &bankDatabase; // reference to the account info database
26 }; // end class Withdrawal
27
28 #endif // WITHDRAWAL_H

Software Engineering Observation 9.12

Several UML modeling tools can convert UML-based designs into C++ code, considerably speeding the implementation process. For more information on these "automatic" code generators, refer to the Internet and Web resources listed at the end of Section 2.8.

This concludes our discussion of the basics of generating class header files from UML diagrams. In the final "Software Engineering Case Study" section (Section 3.11), we demonstrate how to modify the header files to incorporate the object-oriented concept of inheritance.

Software Engineering Case Study Self-Review Exercises

9.1

State whether the following statement is true or false, and if false, explain why: If an attribute of a class is marked with a minus sign (-) in a class diagram, the attribute is not directly accessible outside of the class.

9.2

In Fig. 9.21, the association between the ATM and the Screen indicates that:

  1. we can navigate from the Screen to the ATM
  2. we can navigate from the ATM to the Screen
  3. Both a and b; the association is bidirectional
  4. None of the above
9.3

Write C++ code to begin implementing the design for class Account.

Answers to Software Engineering Case Study Self-Review Exercises

9.1

True. The minus sign (-) indicates private visibility. We've mentioned "friendship" as an exception to private visibility. Friendship is discussed in Chapter 10.

9.2

b.

 
9.3

The design for class Account yields the header file in Fig. 9.27.

Figure 9.27. Account class header file based on Fig. 9.20 and Fig. 9.21.

 1 // Fig. 9.27: Account.h
 2 // Account class definition. Represents a bank account.
 3 #ifndef ACCOUNT_H
 4 #define ACCOUNT_H
 5
 6 class Account
 7 {
 8 public:
 9 bool validatePIN( int ); // is user-specified PIN correct?
10 double getAvailableBalance(); // returns available balance
11 double getTotalBalance(); // returns total balance
12 void credit( double ); // adds an amount to the Account
13 void debit( double ); // subtracts an amount from the Account
14 private:
15 int accountNumber; // account number
16 int pin; // PIN for authentication
17 double availableBalance; // funds available for withdrawal
18 double totalBalance; // funds available + funds waiting to clear
19 }; // end class Account
20
21 #endif // ACCOUNT_H


Introduction to Computers, the Internet and World Wide Web

Introduction to C++ Programming

Introduction to Classes and Objects

Control Statements: Part 1

Control Statements: Part 2

Functions and an Introduction to Recursion

Arrays and Vectors

Pointers and Pointer-Based Strings

Classes: A Deeper Look, Part 1

Classes: A Deeper Look, Part 2

Operator Overloading; String and Array Objects

Object-Oriented Programming: Inheritance

Object-Oriented Programming: Polymorphism

Templates

Stream Input/Output

Exception Handling

File Processing

Class string and String Stream Processing

Web Programming

Searching and Sorting

Data Structures

Bits, Characters, C-Strings and structs

Standard Template Library (STL)

Other Topics

Appendix A. Operator Precedence and Associativity Chart

Appendix B. ASCII Character Set

Appendix C. Fundamental Types

Appendix D. Number Systems

Appendix E. C Legacy Code Topics

Appendix F. Preprocessor

Appendix G. ATM Case Study Code

Appendix H. UML 2: Additional Diagram Types

Appendix I. C++ Internet and Web Resources

Appendix J. Introduction to XHTML

Appendix K. XHTML Special Characters

Appendix L. Using the Visual Studio .NET Debugger

Appendix M. Using the GNU C++ Debugger

Bibliography



C++ How to Program
C++ How to Program (5th Edition)
ISBN: 0131857576
EAN: 2147483647
Year: 2004
Pages: 627

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net