O'Reilly Network    
 Published on O'Reilly Network (http://www.oreillynet.com/)
 See this if you're having trouble printing code examples


What Is Prefactoring

by Ken Pugh, author of Prefactoring
11/15/2005
Prefactoring
Prefactoring is the application of past experience to the creation of new software systems. Past experience includes both your own and that of others, particularly lessons learned from refactoring. The experience is captured in a number of guidelines that can be easily applied to the development process.

In This Article:

  1. Extreme Abstraction
  2. Extreme Separation
  3. Extreme Readability
  4. Testing
  5. Retrospectives
  6. Summary

You probably have heard of refactoring, the process of restructuring code without changing its external behavior. Refactoring was made popular by Martin Fowler and his book, Refactoring: Improving the Design of Existing Code. Prefactoring does not imply that you will never need to refactor your code. But it may help you to create more readable and maintainable code, as well as to make design decisions that are more amenable to refactoring.

Prefactoring has a number of guidelines that suggest ways to proceed with your development. The guidelines have been gathered from the experiences of many programmers over the years. My book, Prefactoring, lists guidelines in the areas of Extreme Abstraction, Extreme Separation, and Extreme Readability, as well as general ones. I'll cover a few of the guidelines in this article.

Extreme Abstraction

The "When You're Abstract, Be Abstract All the Way" guideline states that you should define all your data with abstract data types, rather than primitive data types. Abstract data types usually have additional behavior that can be encapsulated within the type. For example, any variable or field that represents a dollar amount should be declared as a Dollar, rather than a double. The Dollar type can include functionality for converting it to and from a string, as well as appropriate arithmetic functions such as add and subtract. The class might appear as:

    class Dollar
        {
        String toString()
            {
            // Format output according to corporate
            // standards, e.g. use minus sign or
            // parentheses for negative values
            }
        static Dollar parseString(String in)
            throws InvalidFormatException
            {
            // Parse string, throwing exception if
            // not formatted according to corporate
            // standards
            }
        Dollar add(Dollar value)
            {
            // Add dollar value
            }
        Dollar multiplyRoundDown(double multiplier)
            {
            // Round the result according to
            // the business rules
            }
        //. Other methods relating to Dollar
        }

With the functionality and details contained within the data type, you are less likely to create duplicate code. If you find you need additional functionality for Dollar, you already have the class in which to put it.

In addition, with abstract data types, your code becomes more readable. You can concentrate on the solution to the overall problem, rather than the specific details. For example, consider:

    Dollar discount = amount.multiplyRoundDown
        (DISCOUNT_PERCENTAGE);
    System.out.println("Discount is " + discount); 

Without the Dollar type, the code might have read something like:

    double discount = round
        (amount * DISCOUNT_PERCENTAGE, 2);
    System.out.println("Discount is " +
        toDollarFormat(discount)); 
Prefactoring

Related Reading

Prefactoring
By Ken Pugh

Extreme Separation

The guideline "If It Has Collection Functionality, Make It a Collection" advises that a field or attribute that represents an aggregation, such as an array, should be separated in its own class. For example, suppose you had an attribute and a method as:

    class Customer
        {
        Dollar purchases[]; 
        // . other attributes and methods
        Dollar average_purchases(); 
        }

In average_purchases(), you create a loop or use an iterator, or employ a language-dependent mechanism. That places code inside of Customer that is concerned with manipulating only Dollars. Instead, you could make the Dollar array into a collection, as:

    class DollarCollection
        {
        // "Collection Functionality" 
        Dollar average() {}
        Dollar smallest() {}
        Dollar largest() {}
        Dollar total() {} 
        // Plus standard collection methods, such as 
        addDollar(Dollar aDollar){}
        //. 
        }

Now the class looks like:

    class Customer
        {
        DollarCollection purchases;
        }

And the code to determine the average is simply:

    Dollar average_purchases()
        {
        return purchases.average();  
        }

Inside of DollarCollection, you can use whatever mechanism you want to hold the elements of the collection -- an array, a Vector, or even a database table. This separation of implementation from use makes the code easier to maintain.

Extreme Readability

The "Use the Client's Language" guideline suggests that the code that embodies business rules should be readable by the client (the person providing the specifications for a system). The rules should use the terminology of the client. The logical presentation of the code should match as closely as possible the flow as provided by the client. For example, a client might want to offer discounts for purchases over a particular amount. So the code could read:

    Percentage discount = 0.0;
    if (total_of_purchase() > PURCHASE_LIMIT)
        discount = PURCHASE_DISCOUNT;
    if (total_of_purchases_in_last_year() >
        PURCHASE_YEARLY_LIMIT)
        discount += ADDITONAL_YEARLY_DISCOUNT; 

With readable code, the client has the opportunity to review the business rules to check that they have been correctly translated. Changes in the rules become easier, as the client can mark up the actual code with the alterations.

Testing

The importance of testing has been re-accentuated by the Extreme Programming movement. The guideline "Plan for Testing" suggests exploring how you are going to test the system before you decide on the internal design. Starting with the use cases, you can develop outlines for tests that verify proper system operation. You may also develop misuse cases, which state the ways a user may unintentionally or intentionally abuse a system. For example, if you were creating a system to give discounts on purchases, a misuse case could be entering a discount greater than 100 percent or less than 0 percent.

Creating the tests and reviewing them with the client may help you discover any unclear, missing, or ambiguous requirements. For example, the tests for the discount misuse case are to enter values greater than 100% and less than 0% and see if they are rejected. Reviewing these tests with the client might reveal other rules, such as the sum of all discounts should not exceed a certain number. Clarifying the requirements before design always helps to reduce the amount of code changes.

Retrospectives

Part of the process of prefactoring is continually examining why you have needed to refactor your code or make other changes in your design. Performing a retrospective after each release may provide you ideas on design alternatives that may better fit your project. (see Norm Kerth's Retrospectives, or Project Retrospectives: A Handbook for more information). Designs that are more congruent to the underlying concepts can make it easier to implement the requirements for the next iteration.

The retrospective may also reveal new guidelines that you can employ in your development work.

Summary

Applying the experience embodied in the prefactoring guidelines may ease the development of your next programming project and reduce the need for refactoring.

Ken Pugh has extensive experience in the area of software analysis and design, both as a doer and as a teacher. He's a well-known, frequent conference speaker, and the author of Prefactoring, from O'Reilly.


Return to the O'Reilly Network

Copyright © 2009 O'Reilly Media, Inc.