Inside a Refactoring


One of the defining aspects of refactoring is the focus on safe transformations. We'll walk through a simple refactoring. Along the way we'll derive some guidelines that will help us better understand how refactorings work.

Consider the refactoring Encapsulate Field. Its goal is to make clients of an object use methods to access a field rather than having them access the field directly:

 public String name;  private String name; public String getName() {return name;} public void setName(String newName) {name = newName;} 

Martin Fowler's catalog tells us to

  1. Create getters and setters for the field.

  2. Locate all references; replace accesses with calls to the getter and changes to the field with calls to the setter.

  3. Compile and test after changing each reference.

  4. Declare the field as private.

  5. Compile and test.

Refactoring is a step-by-step process. The steps are smaller than people typically expect. Most refactorings tend to take from a minute to an hour to apply; the average is probably five to ten minutes. So, if a refactoring takes a few minutes, the steps are even smaller.

Refactoring works in tiny steps.


Initially, we have:

 public class Person {public String name;} 

The test client looks like this:

 Person person; person.name = "Bob Smith"; assertEquals("Bob Smith", person.name); 

Step 1: Create getter and setter methods.

 public class Person {    public String name;    public String getName() {return name;}    public void setName(String newName) {name = newName;} } 

Note that the client is unchanged: no code is calling these new methods yet.

Step 2: Find all clients; replace references with calls. Do this one at a time.

One way to find those references is to temporarily make the field private; if you do this, put it back to public scope before changing the clients so you don't break any clients.

In many refactorings, the compiler will tell you if things are going right. For example, suppose you're using Hide Method to make a public method be private. When you change the visibility keyword and recompile, you get a warning that some client is still using the method. Because the warning is at compile-time instead of an error at run-time, you're protected while you refactor.

The compiler talks to you.


Assignment

 person.name = "Bob Smith"; 

becomes

 person.setName("Bob Smith"); 

Step 3: Compile and test.

Even though refactorings have the goal of having an improved system at the end of the refactoring, many of them have bases, safe points , or interruption points along the way (think of bases in baseball or the children's game of tag; they may not be the ultimate destination, but at least you can't get tagged while you're on the base). Since it embodies two different approaches in the midst of refactoring, the system is not as clean as it will be in the end, but we can stop, run the tests, and make sure we're OK so far.

Most refactorings have built-in bases .


I imagine holding my breath while the system is in an unsafe state and then letting it go when the tests run correctly. This mild tension and release feels so much better than the feeling you get where you're halfway through one thing and you realize you want to do something else before you finish, and so on, and so on, until you're juggling five balls instead of one.

Large refactorings use this idea of bases as well. It's even more important there. If it will take months to clean out the remnants of some decision, we must have safe points along the way.

Step 2: Replace the other reference.

Reference

 assertEquals("Bob Smith", person.name); 

becomes

 assertEquals("Bob Smith", person.getName()); 

Step 3: Compile and test.

If we absolutely had to, we could stop after either occurrence of step 3 and still be safe. The code would be worse than when we started in one sense: It uses two inconsistent approaches to access the field. Nevertheless, it is progress toward a better approach.

Step 4: Once all clients are changed, make the field private.

 public class Person {    private String name;    public String getName() {return name;}    public void setName(String newName) {name = newName;} } 

Step 5: Compile and test one last time.

Again, we're safe, and the field is completely encapsulated.



Refactoring Workbook
Refactoring Workbook
ISBN: 0321109295
EAN: 2147483647
Year: 2003
Pages: 146

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