TrainingConferencesAbout UsContact UsAdvertiseSQE.comRSS Feed

StickyMinds.com: brain food for building better software

Log In
 Clarify Your Search Criteria

Tips on Using Our Search Feature(s)
 
StickyMinds.com Home
ResourcesTopicsCommunityPowerPassBlogs
Home  >  Detail: A Path to Readable Code



A StickyMinds.com Original

A Path to Readable Code

By Ken Pugh

Send This Content to a FriendGet a Short Link to This ContentPrint This ContentSee User Comments About This Content

Summary: Test-driven development is usually presented as a developer process. On the other hand, acceptance test-driven development (ATDD) is a communication process between the customer and the developer. In ATDD, the tests provide the terminology in customer-understandable terms. The customer's terminology suggests abstract data types that make code more readable.


Infosys
Recently I was reviewing the agile development process at a client where the development group was implementing acceptance test-driven development. The client's agile teams include both a business analyst and a system tester. Working together, they are developing acceptance tests using the Framework for Integrated Testing (FIT). FIT tests are represented by tables embedded within HTML pages. Each table specifies test cases consisting of values of inputs and expected outputs.

When creating test cases, text-based business rules are converted to tables that give examples of those rules. For instance, suppose the business rule for giving a discount is:

If Customer Rating is Good and the Order Total is less than or equal $10.00,
          Then do not give a discount,
          Otherwise give a 1% discount.
If Customer Rating is Excellent,
          Then give a discount of 1% for any order.
If the Order Total is greater than $10.00,
          Then give a discount of 5%.


These requirements might be interpreted in multiple ways. For example, if the rating is excellent and the order is greater than $10.00, is the discount 1 percent or 5 percent? Using a table to illustrate the rule can decrease misinterpretation.



Table 1 gives examples of this business rule.

The FIT framework transforms this table into calls to a fixture. Fixtures convert the values in an HTML table to assignments to variables and the execution of methods. User-written fixtures inherit from predefined classes provided by the FIT framework. The variables and methods are defined by the column headers. A header with the "()" suffix denotes a method. For each row in a table that references a column fixture, FIT sets the variables, executes the method, and compares the returned result to the expected value in the method column.

The name of the fixture DiscountFixture is the first line of the table. The fixture has public data members with the names orderTotal and customerRating and a public method called discountPercentage(), which returns a percentage (implemented as a double). Listing 1 shows how this fixture would be implemented in Java.



Assume that CustomerRating is an enumeration with a method that converts a string such as "Good" to a constant as CustomerRating.GOOD;.

Creating this table in advance of coding helps clarify the requirements of the system. It also helps the developer create a readable set of tests. In my book Prefactoring [1], I give some guidelines for readability. One of them is to "Use the Client's Language." Within the FIT table, the client has specified the requirements in his own language. The names of the table columns should be used in the code that implements the business rules. For example, the method should look something like:

double computeDiscount(double orderTotal,
        CustomerRating customerRating);

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) {}

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 implementation details and keeps the formatting and string interpretation in one place. Following the "Don't Repeat Yourself" rule makes for consistency. Who should better know how to format a Dollar than the Dollar class itself?

Simply multiplying a double by a double that yields a double does not handle all real-life issues. When you create a method called multiplyBy (Percentage other), then you may have the impetus to discuss with the client what issues are involved. In implementing the method, you may realize that rounding is involved and you are not sure how to deal with it, so ask the client.

Suppose the client responds that rounding is a case of "Well, it all depends on the context." Then, you can either create two methods (multiplyByRoundUp and multiplyByRoundDown) or add a parameter (called RoundingType having values ROUND_UP or ROUND_DOWN) to multiplyBy(). In addition, having a multiplyBy() method permits a single place where you can accumulate all the pennies from rounding, so you can save them for a rainy day.

And what about Percentage? Having a data type of that kind should make it clearer that percentages should be expressed in terms of 1.0 percent rather than as a fraction of one (e.g., .01).

Before we conclude, let's go abstract all the way. Let's create a data type called Count. We'll need to add some arithmetic methods to Count, as shown in listing 7.



We may find a need to add a method to Dollar called Dollar multiplyBy(Count other)—and maybe even a few more. But every time we do, we'll be encapsulating another implementation, and separating the implementation from the use will make your code more readable.

For business-related programs, you'll probably have a small number of data types, such as Dollar, Percentage, and Count, that you'll use over and over again. The work involved in creating a set of data types and the associated arithmetic methods will pay off. For scientific programs, you may find the number of data types keeps growing. Every new data type may require more arithmetic methods to interact with the existing data types, such as dividing a length type by a time type to get a speed type. But chances are there are more limited interactions between these data types, and the necessary arithmetic methods will not rapidly approach unmanageable levels.

Creating acceptance tests not only helps define the requirements but also helps make the code more readable to the client. Using the client's language makes the translation between client expectations and developer implementation much simpler. {end}

References
[1] Pugh, Ken. Prefactoring. O’Reilly Media, Inc, 2005.
[2] Mugridge, Rick and Cunningham, Ward. Fit for Developing Software. Prentice Hall, 2005.

What is your approach to dealing with the Dollar and Percentage concepts?

Join the conversation below or start a new one in the Member Comments section.


About the Author
Ken Pugh is a fellow consultant with Net Objectives. He consults, trains, mentors, and testifies on technology topics ranging from object-oriented design to Linux/Unix to system development processes. Ken has written several programming books, including the Jolt Award-winning, Prefactoring, and has served clients from London to Sydney. When not computing, Ken enjoys snowboarding, windsurfing, biking, and hiking the Appalachian Trail. Contact Ken at ken.pugh@netobjectives.com.

Back to Top
 
 

Member Comments
Add Your Comment
 
Comment:    
by Igor Kikena 11/3/2009

Use the golden rule: "Simplicity always beats complexity". The code in the example might be readable, but it's a nightmare to maintain it (ex: other math operators on the dollar class), unless developer has no concept of a budget or a due date.


 
Back to Top



 
Ads By Google
What's This?
 
 



Home   |   Resources   |   Topics   |   Community   |   PowerPass



© 2010 StickyMinds.com. All rights reserved.
StickyMinds.com is a division of Software Quality Engineering.
Privacy Policy    Terms & Conditions    Link to StickyMinds.com    Feedback


ThoughtWorks




STARWEST 

Agile Development Practices