14.3. The Third Store

 <  Day Day Up  >  

After Tim and I delivered to Sam the release containing the changes accommodating a second store, he gave me a call. "Well, I've got even better news," he said.

"The second store has been successful, so I'm thinking about expanding into either Canada or Mexico. How much of an effort will be required to make that change?"

"Are you talking Cozumel?" I asked.

"Well, I was thinking about that or Whistler. It'd be tough to have to go on a business trip to check out how the stores were doing," he replied.

"I think these systems will need a developer to install them," I stated.

"Only if it comes out of your pocketbook," he answered .

14.3.1. Currency Flexibility

If Sam picks Whistler (in British Columbia), Tim and I will not have to worry about translating the program into another language. We might even get away without any changes, since Canadians use dollars, albeit different ones from the U.S.

For an initial examination, we looked at all the places where the Dollar class is used. Since we have the separate class (and have not defined it as a double), it is easy to determine which attributes or variables represent dollars. We could add a couple of classes such as CandianDollar and MexicanPeso . However, that does not feel right. This sounds like a place to start adding some flexibility ("More Is Sometimes Less"). As long as we are going to add two variations, we might as well be prepared for more.

Tim and I change the Dollar class into a Money class. That adjustment should provide the limberness to adapt to either of the proposed expansion locations. We start by creating a single class that parallels the Dollar class:

 enumeration CurrencyID         {US_DOLLARS, CANADIAN_DOLLARS, MEXICAN_PESO}     class Money         CurrencyID id         Money(CurrencyID id)         // Same operations and attributes as for Dollar. 

Currencies differ only by data, not by behavior. So creating a single class makes sense (" Don't Overclassify"). The data differences include the symbol used for display, the side (left or right) on which the symbol is placed, and the format of the number itself (decimal point and thousands separator).

Sam's systems operate independently, so each system need only deal with the local currency. The actual currency can be read in from a configuration file, or we might obtain it from the locale. If we have to worry about exchanging currencies, we can create a CurrencyExchange class that transforms a Money amount in one currency to a Money amount in another currency.

Tim and I started by making up identifiers for the individual currencies. Since currencies are universal, we did a quick search on Google? to see if a standard set of identifiers exists. We found the ISO 4217 set of currency identifiers. So the enumeration changes to this:

 enumeration CurrencyID {USD, CAD,MXN} 

At the same time, we found a Currency class for Java. Since we began the implementation in Java, we will use that class ("Don't Reinvent the Wheel") to designate the currency in the Money class. We also found a Java CurrencyFormat class to which we can delegate the job of properly formatting Money . If we were coding in another language, we might still use those class interfaces and create an implementation for them in that language.

Now all we need to do is to substitute Money for Dollar everywhere we find Dollar in the current system. Then we try the system using tests for Money that parallel the Dollar tests.

If we had started our design by dealing with Money in the beginning, we might have been bogged down in the details of exchange rates and so forth. Since Sam's systems were being used in only one country, we coded for that currency only. Creating a more generalized monetary package would have been premature. Using the experience gained in developing the specific Dollar class helps us to design the Money class.

AVOID PREMATURE GENERALIZATION

Solve the specific problem before making the solution general .


You might find generalizations you want to develop before you need them. Write down your thoughts on the generalization in your design journal. You might heed Brad Appleton's suggestion (http://c2.com/cgi/wiki?PrematureGeneralization):

Whenever I get such ideas to generalize this way, I don't necessarily do it. What I do is try to make sure I don't make any other design decisions which would preclude me from adding it later on. If I can't do that, then I at least try to do it in a way that doesn't require a complete overhaul if I need to go back and change it.

14.3.2. Language Flexibility

With multiple countries , we have to deal with more than just currency differences. Either Sam has to hire people who can read English, or the system has to present its displays and reports in the user 's choice of French, English, or Spanish. When we coded our classes, we used the "Never Write a Constant in Code" guideline. We separated the contents of what was displayed from the reason for displaying it. For example:

 String ERROR_CD_DISC_NOT_IN_COLLECTION =             "CDDisc PhysicalID not in collection";. 

There are at least two approaches to coding additional languages. This is another example of "The Spreadsheet Conundrum." We could provide translations of the strings in each class. A call to a configuration parameter could tell which strings to display. The pseudocode for retrieving strings would look like this:

 CommonString error_string = get_string(CD_DISC_NOT_IN_COLLECTION); 

The method would look like this:

 enumeration StringID {         CD_DISC_NOT_IN_COLLECTION,         CUSTOMER_NOT_IN_COLLECTION};     CommonString get_error_string (StringID which_string)         {         static CommonStrings strings [][NUMBER_LANGUAGES] =             {"English CD Not..", "French CD Not..", "Spanish CD Not..."},             {"English Customer Not...", "French Customer Not ..",                 "Spanish Customer Not..."};         index  = Configuration.get_index_for_current_language( );         return strings[which_string][index]         } 

Another way to handle multiple languages is to create resource files. Each resource file contains all the strings for the entire program for one language . When the program is loaded, the resource file specified by the configuration is also loaded.

Most systems are developed using the resource file approach. From a translation standpoint, having a single file containing the content to be translated is much easier than having multiple files. However, every resource file will need to be altered every time a string is added or changed in a class. If the number of languages does not change, but the number of classes with strings changes frequently, keeping strings in the individual classes will localize the alterations required.

Unfortunately, many integrated development environments (IDEs) do not support multiple languages directly. In the Java implementation in Chapter 8, the IDE places strings directly into the code. To support multiple languages, we can alter the IDE-generated code to refer to a variable that represents the string.

Language is not the only thing that changes between countries. For example, the date format changes. With a single change in a configuration file, we would like to have everything change for us. The manner in which information is displayed is separate from the decision of what to display ("Separate Concerns to Make Smaller Concerns "). Many languages and operating systems support the concept of locale, so we incorporate that into our configuration mechanism, instead of creating a custom one.

 <  Day Day Up  >  


Prefactoring
Prefactoring: Extreme Abstraction, Extreme Separation, Extreme Readability
ISBN: 0596008740
EAN: 2147483647
Year: 2005
Pages: 175
Authors: Ken Pugh

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