Software Design Case Study - Currency Conversion
This was a simple introductory class assignment in C programming, originally for the days of MS-DOS and command lines.. I've used it from time to time as a WIN32 console application, to demonstrate functional decomposition and design-by-comments. The assignment was simple enough, but I rewrote the original requirements as a series of use cases, to facilitate design and testing. The overall goals were to:
Not all countries have the concept of pennies, shillings, or other coinage; therefore the program will assume that all currency input is in integer amounts and the output will be in decimal amounts (e.g. $1.3782).
The output results are to be placed into an array. For simplicity, the maximum size of the array will be set at compile time. The output array will be used as a circular buffer, so that it holds the last 10 conversions.
The format of the currency input file will be ASCII text, either comma or tab separated. The file will contain one line for each currency. Each line will contain the currency name and the conversion to US Dollars (e.g number of Yen per Dollar). The program need only handle up to 20 currencies in the file. When reading currencies from the file, menu letters for each currency will be assigned automatically.
The program will be a WIN32 Console application running from a command line.
Initially, I identified those parameters that were most likely to change:
As a result, I defined a data file format that would define the country and exchange rate. The program would simply read lines from the file until it either ran out of entries or hit the 20th entry, writing each country's information to a data structure in an array. Menu letters were assigned automatically as the countries were read from the file. This design accommodated the primary source of change in this application.
DO NOT FLOWCHART!
Functional Decomposition often encourages the use of flow charts. THIS IS A VERY BAD PRACTICE! Flowcharting is a hangover from the days of unstructured programming. Flowcharting was developed when branching was still done by the conditional GOTO: [ IF (condition) GOTO X ]. It is easy to flowchart a logic sequence that either cannot, or should not, be coded.
Instead, I encourage design-by-comments at the function level, and UML for object-oriented design at the application level. Because this was such a small program being written in C, I used functional decomposition exclusively. I began the functional decomposition with the main() function:
This was a console application, so the main() function is called when the program starts. When main() exits, the program ends. I created the project and did the functional decomposition using comments. The initial decomposition was:
/* Initialize program data
structures and variables */
The initial decomposition of the menu selection function was:
/* WHILE a valid
menu item has NOT been selected DO */
Code was then written under each comment: a function call to perform the required action and return the required result. The functions were written as stubs (function just returns a hard coded value) to allow the program to compile and to initially test the logic in main(). The conversion function was coded first, and the other functions remained stubs, with the menu selection functions being stubbed to return a hard coded menu selection. That allowed unit testing to proceed incrementally, rather than waiting until the entire program was written.
One the functions were designed in comments, code was added beneath the comments. Additional code for error handling had to be added in some cases, such as failure to open a currency definition file. The design was done in comments, and stubbed so that no errors would be caught. Once the main logic was working, code was written to implement the error handling. This is termed an Incremental Life Cycle.
The design can be read in the comments of the source code.
Because the program was designed first, using functional decomposition, and because functions were initially stubbed to return a hard coded value, unit testing could be done incrementally, one function at a time. This allowed early detection of defects. Also, if fundamental logic errors were found, they could be corrected before very much code was written.
The basic set of tests were: