11.8. (Optional) Software Engineering Case Study: Incorporating Inheritance and Polymorphism into the ATM SystemWe now revisit our ATM system design to see how it might benefit from inheritance and polymorphism. To apply inheritance, we look for commonality among classes in the system. We create an inheritance hierarchy to model similar classes in an efficient and elegant manner that enables us to process objects of these classes polymorphically. We then modify our class diagram to incorporate the new inheritance relationships. Finally, we demonstrate how to translate the inheritance aspects of our updated design into Visual Basic code. In Section 4.9, we encountered the problem of representing a financial transaction in the system. Rather than create one class to represent all transaction types, we created three distinct transaction classesBalanceInquiry, Withdrawal and Depositto represent the transactions that the ATM system can perform. The class diagram of Fig. 11.17 shows the attributes and operations of these classes. Note that they have one private attribute (accountNumber) and one public operation (Execute) in common. Each class requires attribute accountNumber to specify the account to which the transaction applies. Each class contains operation Execute, which the ATM invokes to perform the transaction. Clearly, BalanceInquiry, Withdrawal and Deposit represent types of transactions. Figure 11.17 reveals commonality among the transaction classes, so using inheritance to factor out the common features seems appropriate for designing these classes. We place the common functionality in base class transaction and derive classes BalanceInquiry, Withdrawal and Deposit from transaction (Fig. 11.18). Figure 11.17. Attributes and operations of classes BalanceInquiry, Withdrawal and Deposit.Figure 11.18. Class diagram modeling the generalization (i.e., inheritance) relationship between the base class TRansaction and its derived classes BalanceInquiry, Withdrawal and Deposit.The UML specifies a relationship called a generalization to model inheritance. Figure 11.18 is the class diagram that models the inheritance relationship between base class TRansaction and its three derived classes. The arrows with triangular hollow arrowheads indicate that classes BalanceInquiry, Withdrawal and Deposit are derived from class TRansaction by inheritance. Class transaction is said to be a generalization of its derived classes. The derived classes are said to be specializations of class transaction. In Fig. 11.17, you saw that classes BalanceInquiry, Withdrawal and Deposit share private Integer attribute accountNumber. We'd like to factor out this common attribute and place it in the base class transaction. However, recall that a base class's private attributes are not accessible in derived classes. The derived classes of TRansaction require access to attribute accountNumber so they can specify which Account to process in the BankDatabase. As you learned in Chapter 10, a derived class can access only the Public and Protected members of its base class. However, the derived classes in this case do not need to modify attribute accountNumberthey need only to access its value. For this reason, we have chosen to replace private attribute accountNumber in our model with the public ReadOnly property AccountNumber. Since this is a public ReadOnly property, it provides a only Get accessor to access the account number. Each derived class inherits this property, enabling the derived class to access its account number as needed to execute a transaction. We no longer list accountNumber in the second compartment of each derived class, because the three derived classes inherit property AccountNumber from TRansaction. According to Fig. 11.17, classes BalanceInquiry, Withdrawal and Deposit also share operation Execute, so base class TRansaction should contain Public operation Execute. However, it does not make sense to implement Execute in class transaction, because the functionality that this operation provides depends on the specific type of the actual transaction. We therefore declare Execute as an abstract operation in base class transaction (i.e., it will become a MustOverride method in the Visual Basic implementation). This makes TRansaction an abstract class (i.e., MustInherit class) and forces any class derived from TRansaction that must be a concrete class (i.e., BalanceInquiry, Withdrawal and Deposit) to implement the operation Execute to make the derived class concrete. The UML requires that we place abstract class names and abstract operations in italics. Thus, in Fig. 11.18, transaction and Execute appear in italics for the transaction class; Execute is not italicized in derived classes BalanceInquiry, Withdrawal and Deposit. Each derived class overrides base class TRansaction's Execute operation with an appropriate concrete implementation. Note that Fig. 11.18 includes operation Execute in the third compartment of classes BalanceInquiry, Withdrawal and Deposit, because each class has a different concrete implementation of the overridden operation. As you learned in this chapter, a derived class can inherit "interface" and implementation from a base class. Compared to a hierarchy designed for implementation inheritance, one designed for interface inheritance tends to have its functionality lower in the hierarchya base class signifies one or more operations that should be defined by each class in the hierarchy, but the individual derived classes provide their own implementations of the operation(s). The inheritance hierarchy designed for the ATM system takes advantage of this type of inheritance, which provides the ATM with an elegant way to execute all transactions "in the general" (i.e., polymorphically). Each class derived from transaction inherits some implementation details (e.g., property AccountNumber), but the primary benefit of incorporating inheritance into our system is that the derived classes share a common interface (e.g., abstract operation Execute). The ATM can aim a transaction reference at any transaction, and when the ATM invokes the operation Execute through this reference, the version of Execute specific to that transaction runs automatically. For example, suppose a user chooses to perform a balance inquiry. The ATM aims a transaction reference at a new object of class BalanceInquiry, which the Visual Basic compiler allows because a BalanceInquiry is a TRansaction. When the ATM uses this reference to invoke Execute, BalanceInquiry's version of Execute is called. This polymorphic approach also makes the system easily extensible. Should we wish to create a new transaction type (e.g., funds transfer or bill payment), we would simply create an additional TRansaction derived class that overrides the Execute operation with a version appropriate for the new transaction type. We would need to make only minimal changes to the system code to allow users to choose the new transaction type from the main menu and for the ATM to instantiate and execute objects of the new derived class. The ATM could execute transactions of the new type using the current code, because it executes all transactions identically (through polymorphism). As you learned earlier in the chapter, an abstract class like transaction is one for which you never intend to (and, in fact, cannot) instantiate objects. An abstract class simply declares common attributes and behaviors for its derived classes in an inheritance hierarchy. Class transaction defines the concept of what it means to be a transaction that has an account number and can be executed. You may wonder why we bother to include abstract operation Execute in class transaction if Execute lacks a concrete implementation. Conceptually, we include this operation because it is the defining behavior of all transactionsexecuting. Technically, we must include operation Execute in base class transaction so that the ATM (or any other class) can invoke each derived class's overridden version of this operation polymorphically via a transaction reference. Derived classes BalanceInquiry, Withdrawal and Deposit inherit property AccountNumber from base class transaction, but classes Withdrawal and Deposit contain the additional attribute amount that distinguishes them from class BalanceInquiry. Classes Withdrawal and Deposit require this additional attribute to store the amount of money that the user wishes to withdraw or deposit. Class BalanceInquiry has no need for such an attribute and requires only an account number to execute. Even though two of the three transaction derived classes share the attribute amount, we do not place it in base class transactionwe place only features common to all the derived classes in the base class, so that derived classes do not inherit unnecessary attributes (and operations). Figure 11.19 presents an updated class diagram of our model that incorporates inheritance and introduces abstract base class transaction. We model an association between class ATM and class transaction to show that the ATM, at any given moment, either is executing a transaction or is not (i.e., zero or one objects of type transaction exist in the system at a time). Because a Withdrawal is a type of transaction, we no longer draw an association line directly between class ATM and class Withdrawalderived class Withdrawal inherits base class transaction's association with class ATM. Derived classes BalanceInquiry and Deposit also inherit this association, which replaces the previously omitted associations between classes BalanceInquiry and Deposit, and class ATM. Note again the use of triangular hollow arrowheads to indicate the specializations (i.e., derived classes) of class transaction, as indicated in Fig. 11.18. Figure 11.19. Class diagram of the ATM system (incorporating inheritance). Note that MustInherit class name TRansaction appears in italics.We also add an association between class transaction and the BankDatabase (Fig. 11.19). All transactions require a reference to the BankDatabase so they can access and modify account information. Each transaction derived class inherits this reference, so we no longer model the association between class Withdrawal and the BankDatabase. Note that the association between class transaction and the BankDatabase replaces the previously omitted associations between classes BalanceInquiry and Deposit, and the BankDatabase. We include an association between class TRansaction and the Screen because all TRansactions display output to the user via the Screen. Each derived class inherits this association. Therefore, we no longer include the association previously modeled between Withdrawal and the Screen. Class Withdrawal still participates in associations with the CashDispenser and the Keypad, howeverthese associations apply to derived class Withdrawal but not to derived classes BalanceInquiry and Deposit, so we do not move these associations to base class TRansaction. Our class diagram incorporating inheritance (Fig. 11.19) also models classes Deposit and BalanceInquiry. We show associations between Deposit and both the DepositSlot and the Keypad. Note that class BalanceInquiry takes part in no associations other than those inherited from class transactiona BalanceInquiry interacts only with the BankDatabase and the Screen. The class diagram of Fig. 9.22 showed attributes, properties and operations with visibility markers. Now we present a modified class diagram in Fig. 11.20 that includes abstract base class TRansaction. This abbreviated diagram does not show inheritance relationships (these appear in Fig. 11.19), but instead shows the attributes and operations after we have employed inheritance in our system. Note that abstract class name transaction and abstract operation name Execute in class transaction appear in italics. To save space, as we did in Fig. 5.28, we do not include those attributes shown by associations in Fig. 11.19we do, however, include them in the Visual Basic implementation in Appendix J. We also omit all operation parameters, as we did in Fig. 9.22incorporating inheritance does not affect the parameters already modeled in Figs. 7.247.27. Figure 11.20. Class diagram after incorporating inheritance into the system.Software Engineering Observation 11.6
Implementing the ATM System Design Incorporating InheritanceIn Section 9.14, we began implementing the ATM system design in Visual Basic. We now incorporate inheritance, using class Withdrawal as an example.
We discuss the polymorphic processing of transactions in Section J.2 of the ATM implementation. Class ATM performs the actual polymorphic call to method Execute at line 86 of Fig. J.1. ATM Case Study Wrap-UpThis concludes our object-oriented design of the ATM system. A complete Visual Basic implementation of the ATM system in 596 lines of code appears in Appendix J. This working implementation uses all of the key object-oriented programming concepts that we have covered to this point in the book, including classes, objects, encapsulation, visibility, composition, inheritance and polymorphism. The code is abundantly commented and conforms to the coding practices you've learned so far. Mastering this code is a wonderful capstone experience for you after studying the nine Software Engineering Case Study sections in Chapters 1, 39 and 11. Software Engineering Case Study Self-Review Exercises
Answers to Software Engineering Case Study Self-Review Exercises
|