|< Free Open Study >|
11.5 Explicit Case Analysis Due to Accidental Complexity
We now know that we have a verified PIN number on the back of a bank card. What is next? In general, ATMs would like to get a transaction object so that the bank can process it. How can an ATM get a transaction? A classic error is to have the ATM worry about putting up menus, enabling the keypad, getting a special key number, and mapping it to a specific transaction. This would add lots of noncommunicating behavior to the SuperKeypad class. Since the Super-Keypad class has everything it needs to build a transaction (except maybe an account and PIN number), why not let it build the transaction for the ATM? The ATM could get the account and PIN number from the card reader and give them to the SuperKeypad, asking the SuperKeypad to get a transaction.
The SuperKeypad is then faced with the task of getting a transaction. It puts the main menu on the screen (assume the main menu labels key #1 as deposit, key #2 as withdraw, key #3 as balance, and key #4 as transfer), reads a key from the keypad, finds out that special key #2 was pressed, and then what? It is here that many designers assume they have made a mistake. In order to map the key to a transaction object, they are faced with an explicit case analysis. Several heuristics state/imply that explicit case analysis is a bad thing, since it implies that an addition to the system will result in changes to the existing code. This problem is common; whenever an object-oriented model bumps up against a nonobject-oriented system, explicit case analysis is often the result. Our design is not warped梠ur interface is! If we had an object-oriented interface, we would push a button and the desired constructor would run. Adding a new transaction would imply adding a new button with no change to the existing code. Some designers might argue that this is cheating since someone, somewhere is asking, "Did the user touch the screen between these X/Y-coordinates, or those X/Y-coordinates, etc.?" The important point is that the case analysis is pushed outside of our domain. If this sounds like a cheat, then polymorphism is a cheat. Someone, somewhere must be performing case analysis. Talk of jump tables and pointers to functions does not change the fact that it is case analysis. The only relevant question is, "Do you, the developer, need to perform the case analysis in your domain?" If the answer is no, then you can add new items to your system without modifying the existing code. (For a related discussion, see the object-oriented network section in Chapter 9, Section 9.2.)
There are two main solutions to consider in this mapping. Either we let the SuperKeypad collect the information for the transaction as part of its case statement and then build the appropriate transaction object with this information, or we immediately build the appropriate transaction object and let it polymorphically initialize itself. The disadvantage of the first design is that we are doing a large amount of work in each instance of the case statement. It is considered beneficial to minimize this work so that when a developer needs to add a new case, he or she will be modifying a minimal amount of existing code. The disadvantage of the second solution becomes obvious if we ask how a derived class knows how to collect information for filling in its data members. The derived classes of the transaction class will require a display screen and keypad to collect their information (or at least a SuperKeypad). This creates at least one more uses relationship in our system, adding complexity. The developer must now decide if the amount of work being performed in the case statement warrants a new uses relationship. This is very subjective given that there are no quantitative metrics for measuring complexity. My gut instinct in this example is to collect the information and then build the objects. The complexity of collecting information does not warrant a new uses relationship between Transaction and SuperKeypad. This must be decided on a case-by-case basis, and because of its subjective facet, it causes numerous, endless arguments during design critiques. Either solution to this problem results in a fully initialized transaction object (in this case, a Withdraw object) being returned to the ATM.
|< Free Open Study >|