Refactoring I — Basics and Motivation
Jukka Viljamaa

Helsinki, October 2000
Seminar on Programming Paradigms

University of Helsinki
Department of Computer Science

Introduction to Refactoring
There is an old engineering saying: “if it works, don’t fix it.” However, in many situations a software system might seem to work but still need adjusting. For example, you might want to add a feature or port the system to a new environment, but the system just isn’t flexible enough to enable that. In such a situation, most programmers intuitively know what needs to be done — they just restructure the system to enable modifications.
There is always a risk in any modification, though. Every time you change a system, you may also introduce new bugs. That’s why you would like to have a way to be sure your changes won’t introduce unexpected side effects.
Given the well-known complexity and iterative nature of object-oriented framework development, it’s no wonder that the concept of refactoring first emerged in that field during the 1990s. The basic idea of refactoring is to clean up code in a controlled manner in order to minimize the chances of introducing bugs. More precisely, a refactoring can be defined as a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior [Fow99].
In this paper we describe how refactorings can be documented and how they affect a software development process. We discuss reasons for using refactorings and basic mechanisms behind them. A small introductory example is given, too. For elaboration on issues concerning the details of individual refactorings and ways to automate them, see Refactoring II [Väh00] (to be represented later in this seminar).
Documenting Refactorings
The power of refactoring as a method lies in systematically organized documents, which describe proven techniques for enhancing software safely [Opd92]. They illustrate possible pitfalls and suggest ways to avoid them. In Refactoring: Improving the Design of Existing Code [Fow99] Martin Fowler et al. introduce a refactoring catalog where they use a standard format to represent over 70 frequently needed refactorings. Each refactoring has five parts:
•	Name identifies the refactoring and helps to build a common vocabulary for software developers.
•	Summary tells when and where you need the refactoring and what it does. Summary helps you to find a relevant refactoring in a given situation. It also includes source code excerpts or UML diagrams to show a simple before and after scenario.
•	Motivation describes why the refactoring should be done and lists circumstances in which it shouldn’t be used.
•	The mechanics part provides a step-by-step description of how to carry out the refactoring. The steps are as brief as possible to make them easy to follow.
•	Examples illustrate how the refactoring can be employed in real programs.
The catalog includes refactorings for composing methods and handling local variables (e.g. Extract Method, Inline Method, Inline Temp, Replace Temp with Query) as well as for moving features and organizing data (Move Method, Move Field, Hide Delegate, Replace Data Value with Object, Replace Type Code with State/ Strategy). The catalog also discusses conditional expressions (Replace Conditional with Polymorphism, Introduce Null Object), object creation (Replace Constructor with Factory Method) and generalization (Replace Inheritance with Delegation, Form Template Method).
Most of the refactorings are quite simple and their name pretty much reveals their intention. In general, most refactorings are tiny steps that transform traditional procedural designs into more object-oriented ones. The many insightful discussions are the main substance of the catalog, in addition to the concrete instructions on how to refactor in various situations.
As the names of some refactorings suggest, refactorings are quite closely related to design patterns [GHJ95]. A design pattern tells you how to solve a recurring design problem in a disciplined manner in a given context. Refactoring, on the other hand, guides you to enhance your existing implementation so that it reflects a better design. Often this “better design” is a design pattern. So, in many cases, you end up applying a set of refactorings to turn a piece of ad hoc code into an instance of a design pattern.
Refactorings are very useful in developing efficient and flexible application frameworks and they fit well to the iterative framework development process. Refactoring as a technique plays also a major part in extreme programming [Bec99, XPr00].
A Basic Example
Look at the simplified movie rental system below. You can see that the code works (it calculates a rental statement for a customer), but the solution it provides isn’t very elegant. Suppose, for instance, that your client wants you to enhance the system to allow statements to be printed also in HTML format. It’s obvious that the code needs to be restructured to avoid duplicating the statement method.
class Movie {
  public static final int REGULAR = 0;
  public static final int NEW = 1;
  private String _title;
  private int _priceCode;
}

class Rental {
  private Movie _movie;
  private int _daysRented;
}

class Customer {
  private String _name;
  private Vector _rentals = new Vector();
  public String statement() {
    double totalAmount = 0;
    Enumeration rentals = _rentals.elements();
    String result = "Rental record for " + getName() + "\n";
    while (rentals.hasMoreElements()) {
      double thisAmount = 0;
      Rental each = (Rental)rentals.nextElement();
      // determine amounts for each line
      switch (each.getMovie().getPriceCode()) {
        case Movie.REGULAR: thisAmount = 2; break;
        case Movie.NEW: thisAmount = each.getDaysRented() * 3; break;
      }
      result += "\t" + each.getMovie().getTitle() + "\t" + thisAmount + "\n";
      totalAmount += thisAmount;
    }
    return result + "Amount owed is " + totalAmount;
  }
}
Below is the same code after application of Extract Method, Move Method, and Replace Temp with Query refactorings.
The logic for determining the charge for each rental has been extracted to a separate method (getCharge) and moved to the Rental class. Similarly the calculation for the total charge has been extracted (getTotalCharge). At the same time comments have been removed since the method names themselves make the code self-evident. Most of the local variables have become obsolete, too. Note that changing charging won’t affect the routines that print the statements, so it's now easy to write different kinds of statements and reports.

class Movie { ... }

class Rental {
  private Movie _movie;
  private int _daysRented;
  double getCharge() {
    switch (getMovie().getPriceCode()) {
      case Movie.REGULAR: return 2;
      case Movie.NEW: return getDaysRented() * 3;
    }
  }
}

class Customer {
  private String _name;
  private Vector _rentals = new Vector();
  public String statement() {
    Enumeration rentals = _rentals.elements();
    String result = "Rental record for " + getName() + "\n";
    while (rentals.hasMoreElements()) {
      Rental each = (Rental)rentals.nextElement();
      result += "\t" + each.getMovie().getTitle()+ "\t" + each.getCharge() + "\n";
    }
    return result + "Amount owed is " + getTotalCharge();
  }
  private double getTotalCharge() {
    double result = 0;
    Enumeration rentals = _rentals.elements();
    while (rentals.hasMoreElements()) {
      Rental each = (Rental)rentals.nextElement();
      result += each.getCharge();
    }
    return result;
  }
}
You may argue that the changes made above are trivial. Still even this short example combines three refactorings, each with many less than obvious details. For example, the use of local variables has been replaced with the use of method calls and return values. Without the mechanics provided by the refactorings one would have probably needed a couple of trials and errors before getting the changes right.
Of course the refactoring process should be continued in the example above. You would probably like to use Form Template Method to further simplify the statement method. Refactorings for enabling easy additions of new price codes would include Replace Type Code with State/ Strategy and Replace Conditional with Polymorphism.
Bad Smells in Code
What qualities do we expect in good software? It has been suggested that we should aim at developing programs that are easy to read, that have all logic specified in only one place, that allow modifications without endangering existing behavior, and whose conditional logic is expressed as easily as possible. Programs that don’t have those qualities smell bad (a term coined by Kent Beck). In [Fow99] Beck names and describes a number of bad smells and refers to refactorings that can be used to get rid of them.
The mother of all sins in programming is Duplicated Code. It is easy to see why it makes software maintenance a nightmare: you need to make (nearly) the same modifications to many places and it is hard to know when you are done with them. Naturally code duplication increases also the amount of code making systems harder to understand and maintain.
Another major source for bad smells is the organization of classes and methods. They can be too big and complex (Large class, Long Method, Long Parameter List) or too small and trivial (Lazy Class, Data Class). Lack of classic modularity qualities of loose coupling between structures and cohesion within them may also cause bad smells (Inappropriate Intimacy, Feature Envy, Data Clumps). Other sources for bad smells include using too much or too little delegation (Message Chains, Middle Man) and using non-object-oriented control or data structures (Switch Statements, Primitive Obsession).
If you think about the original version of the movie rental example above, you may notice several bad smells. There are instances of Data Class (Movie, Rental), Long Method (statement) and Switch Statements just to name a few.
Basic Techniques Behind Refactorings
How do we introduce good qualities to our software and remove bad smells? One of the basic procedures of refactoring (besides eliminating duplication) is adding indirection.
Indirection in its most fundamental form means defining structures (e.g. classes and methods) and giving them names. Using named structures makes code easy to read because it gives you a way to explain intention (class and method names) and implementation (class structures and method bodies) separately. The same technique enables sharing of logic (e.g., methods invoked in different places or a method in superclass shared by all subclasses). Sharing of logic, in turn, helps you to manage change in systems. Finally, polymorphism (another form of indirection) provides a flexible, yet clear way to express conditional logic.
Like in most techniques, the key to success with indirection is to put just the right amount of it in the right spot. Too much or badly placed indirection results in a fragmented system. Useless indirection is often found in a component that used to be shared or polymorphic, but is not anymore due to changes made during the development process. A refactoring catalog gives you the starting point for deciding where and how much indirection should be used. Basically, if you encounter indirection that’s not paying for itself, you need to take it out.
Refactoring in Software Development Process
Refactoring essentially means improving the design after it has been implemented. It is an inherently iterative method, which implies that it doesn’t fit very well to the traditional waterfall model of software engineering process. With refactoring design occurs continuously during development.
Refactoring can be thought of as an alternative to careful upfront design. This kind of speculative design is an attempt to put all the good qualities into the system before any code is written. The problem with this process is that it so easy to guess wrong. Sometimes extreme programming is regarded as a paradigm which reacts to that observation by skipping the design phase altogether.
Refactoring can, however, be used also in a more conservative way. Instead of abandoning the design phase completely, you move from overly flexible and complex think-about-everything-beforehand designs to simpler ones. This is sensible because you don’t need to anticipate all changes in advance. With refactoring, you can favor simplicity in design because design changes are inexpensive.
When you develop software using refactoring, you divide your time between two distinct activities: refactoring and adding function. When you add function you shouldn’t change existing code. If there is a turn in your development that seems difficult because your implementation does not support the new feature very well, you need to first refactor your system to accommodate the modification.
After adding a function you must add tests to see that the function was implemented correctly [Fow00]. You can even consider tests as explicit requirement documentation for the feature and write them before you add it. Use existing tests to ensure that refactoring did not change behavior or introduce bugs.
Testing should be as easy as possible. That’s why all tests should be made automatic and they should check their own results. This will enable you to test as often as you compile. When you encounter a failure, you can concentrate your debugging in a narrow area of code you added after the last successful tests.
The testing technique that works best with refactoring is class-level white-box testing [Bin99]. A good way to write such unit tests for an object-oriented system is to have a separate test class for each important production class. You can also use a test framework (e.g. JUnit [JUn00]) to handle the test case management and reporting in a standardized way. The test framework should provide flexible ways to combine tests so that they can be run in any order.
Testing should be risk driven. This means that you test those parts of a class that are complex and most valuable. Remember to concentrate your testing on the boundaries of the input range and other special conditions (e.g. missing input, null values). Don’t try to test everything or you might end up testing nothing.
Besides testing, code reviews are known to be valuable in verifying software quality. Refactoring process can be an integral part of reviews, especially if they are conducted in small groups. Refactoring gives you a chance to see the concrete effect of suggested corrections.
Benefits of Refactoring
Refactoring can be used for several purposes. First of all, refactoring helps the code to retain its shape. Without refactoring the design of the program will decay. As people change code (usually without fully understanding the design objectives behind the implementation) it gradually begins to loose its structure. Once the structure gets cluttered, the code becomes harder to understand and so the chances of cluttering the design further increase.
Refactoring makes your code more readable. This is essential for conveying the intention of the code to others. It also makes the code easier to read for yourself. That is equally important since it’s unrealistic to assume that you can remember your intentions for more than few weeks.
You can also use refactoring to grasp the intention of unfamiliar code. When looking at a fragment of code you try to understand what it does. When you find out how the code works, you refactor it to better reflect your understanding of its purpose. After that you can test if the system still behaves as it should. If everything goes well, you have understood and processed a part of the system correctly. If not, you need to get a better understanding of the code fragment at hand.
It may sound counterintuitive, but Fowler claims that the real advantage of refactoring is that it helps you develop software more quickly [Fow99]. It is rather easy to believe that refactoring improves quality and readability of code, but how does it speed up development? You would think that all modifications and iterative nature of refactoring, not to mention the big effort put into testing, would make your development slow.
The secret is that adding new functions and finding bugs is very efficient when you work on a system with a solid design you understand well. Without refactoring your system will begin to decay from the very beginning. That’s why it doesn’t take long for the benefits of keeping your implementation in line with the design to overweigh the overhead of refactoring and associated testing. If you ignore refactoring you end up very soon in a situation where you have trouble inserting new functions because of unwanted and unexpected side effects. Similarly, modifications will become harder because you begin to have instances of duplicated code.
Problems with Refactoring
Although very beneficial in many cases, refactoring isn’t always easy or even useful. For example, systems based on database access (or persistency in general) are known to be hard to change. Most business applications are very closely coupled to the database schema that supports them making it hard to modify either the database or the system built on it. Even if you have a sufficient layering separating the objects from the database, you are still forced to migrate your data when the database schema changes. Data migration is also a very familiar nuisance for anyone who has ever tried to modify a Java application using serialization [Jav00].
Changing an interface in an object-oriented system is always a major change when compared to changing implementation. Unfortunately most refactorings change interfaces. This problem is easy to deal with if you have access to the code that uses the changed interfaces — you just modify those parts too.
The problem is much more sever with published interfaces (i.e. interfaces that are used by people outside your organization or by code that you cannot change). If you must refactor a published interface, you should keep both the old and the new version (at least for some time) and the old version should be tagged deprecated. These complications imply that you shouldn’t publish your interfaces prematurely.
Sometimes you should not refactor a system to begin with. The most common reason for this is that the system needs to be written again from scratch. This is usually due to the fact that the system simply doesn’t work and is so full of bugs that it cannot easily be fixed.
A Critical View on Refactoring
The most obvious argument against refactoring would be that there really isn’t much new to it. All the techniques applied in refactorings have been around for years. On the other hand, there clearly is a demand for an easy-to-use handbook for software transformations and maintenance in general. Refactoring catalogs can well serve that purpose.
More serious disappointment is that, after all, refactoring seems to offer very little support for adaptation and maintenance of large legacy systems. Fowler emphasizes that refactoring should be an integral part of the development process, but gives almost no indication on how to work with complex systems that haven’t been constructed to enable modifications. Some concrete tips on where to begin refactoring and how to proceed would have been helpful.
Fowler claims that refactoring makes redesign inexpensive. This seems rather an exaggerated statement if anything else but the lowest level of design is concerned. Refactorings provide ways to safely switch between implementation mechanics with different characteristics. There is, however, much more to design than choosing a class structure or an object interaction scheme. Fowler’s refactorings don’t deal with higher level matters such as implementation environment selection, distribution strategies, or user interface characteristics.

References
Bec99	Beck K., Extreme Programming Explained: Embrace Change. Addison-Wesley, 1999.
Bin99	Binder R., Testing Object-Oriented Systems: Models, Patterns, and Tools. Addison-Wesley, 1999.
Fow00	Martin Fowler’s home page: patterns, refactoring, extreme programming, unit testing, and UML material and links. http://www.martinfowler.com, 2000.
Fow99	Fowler M. et al., Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999. 
GHJ95	Gamma E., Helm R., Johnson R., Vlissides J., Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.
Jav00	Java 2 SDK, Standard Edition Documentation. http://java.sun.com/j2se/1.3/docs/, 2000.
JUn00	JUnit — Testing Resources for Extreme Programming. http://www.junit.org, 2000.
Opd92	Opdyke W., Refactoring Object-Oriented Frameworks. Ph.D. diss., University of Illinois at Urbana-Champaign, ftp://st.cs.uiuc.edu/pub/papers/refactoring/opdyke-thesis.ps.Z, 1992.
Ref00	Refactoring Home Page. http://www.refactoring.com, 2000.
Väh00	Vähäaho M., Refactoring II. To be represented in seminar on Programming Paradigms, University of Helsinki, Department of Computer Science, 2000.
XPr00	XProgramming.com (extreme programming home page), http://www.XProgramming.com, 2000.