Better Software - January 2009 - (Page 13) Code Craft public class CustomerDiscount { Percentage computeDiscount(Dollar orderTotal, CustomerRating customerRating) { if (customerRating == CustomerRating.GOOD) { if (orderTotal.lessThan(new Dollar(10.00))) return new Percentage(0.0); else return new Percentage(1.0); } if (customerRating == CustomerRating.EXCELLENT) { if (orderTotal.greaterThan( new Dollar(10.00))) return new Percentage(5.0); return new Percentage(1.0); } return new Percentage(0.0); } } Listing 2 public class Dollar { public Dollar(double value) { } public Dollar(String value) {} public static Dollar parse(String string) {} public boolean lessThan(Dollar other) { } public boolean greaterThan(Dollar other) {} } public class Percentage { public Percentage(double value) { } public static Percentage parse(String string) {} } Listing 3 orderTotal $10.00 $10.01 $.01 $10.00 $10.01 Table 2 customerRating Good Good Excellent Excellent Excellent discountPercentage () 0.0% 1.0% 1.0% 1.0% 5.0% In the event that the client specifies the column headers as “ord_tot”, “cust_rat”, and “disc”, then you should use those as the names of the parameters and the method name in the FIT table. That avoids having to do a mental translation of terms during communication between the client and the developers. Now, you can take another step and apply the “When You’re Abstract, Be Abstract All the Way” guideline. The values representing orderTotal and discountPercentage() are often represented by doubles. But that’s an implementation issue. The client refers to them as dollars and percentages. Separating implementation from use guarantees more readable code. So, create classes that represent these abstract data types. The computeDiscount() method is shown in listing 2. This Java may look a little odd to the client (particularly with all those “new” keywords). However, if the terms match those by which the client described the FIT columns or their business rules, the client should be able to understand it easily [2]. Since we’ve transformed doubles into Dollars and doubles into Percentages, we’ll need to create some methods in those classes to implement comparisons, as shown in listing 3. The parse() methods are used by FIT to convert a string into the corresponding data type. With the appropriate conversion method, you can use the formatted strings in the FIT table. This usually makes it easier for the client to read the table. For example, if Dollar parse() could take a dollar sign and Percentage parse() could take a percent sign, then the table could be written as in table 2. As the business rules become more complicated, you’ll probably want to insert them into a table, as shown in listing 4. Now, how can one perform arithmetic with Dollars? Create methods, as in listing 5. You pick one, depending on whether or not you want to make Dollar immutable. What other things might you want to do with a Dollar? You might need to multiply a Dollar by a Percentage. Which gives you: Dollar multiplyBy(Percentage other) {} DiscountByRatingAndOrderTotal = { { CustomerRating.GOOD, new Dollar(0.0), new Percentage(0.0) }, { CustomerRating.EXCELLENT, new Dollar(10.0), new Percentage(1.0)}, //. . .and so forth } Listing 4 These methods may make your code look almost like COBOL, but if you are dealing with business terms, then it may not necessarily be bad for it to look like a “common business oriented language,” as shown in listing 6. Is it worth it to create a class called Dollar? After all, it is just a double (or should it be a BigDecimal?). Having the Dollar class hides these JANUARY/FEBRUARY 2009 BETTER SOFTWARE www.StickyMinds.com 13 http://www.StickyMinds.com
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.