Refactoring


One of the main problems in software development is the high cost of maintaining code. Part of the reason is that code quickly becomes "crufty" or messy, as the result of rushed efforts or just plain carelessness. Your primary job in building software is to get it to work, a challenge that you will resolve by writing tests before you write the code. Your secondary job is to ensure that the code stays clean. You do this through two mechanisms:

  1. ensure that there is no duplicate code in the system, and

  2. ensure that the code is clean and expressive, clearly stating the intent of the code

Throughout Agile Java, you will pause frequently to reflect on the code just written. Anything that does not demonstrate adherence to these two simple rules must be reworked, or refactored, immediately. Even within a "perfect" designan unrealistic goalthe poor implementation of code can cause costly headaches for those trying to modify it.

The more you can continually craft the code as you go, the less likely you are to hit a brick wall of code so difficult that you cannot fix it cheaply. Your rule of thumb should be to never leave the code in a worse state than when you started working on it.

Even within your small example so far, there is some less-than-ideal code. Start on the path to cleaning up the code by taking a look at the test:

 public void testCreate() {    Student student = new Student("Jane Doe");    String studentName = student.getName();    assertEquals("Jane Doe", studentName);    Student secondStudent = new Student("Joe Blow");    String secondStudentName = secondStudent.getName();    assertEquals("Joe Blow", secondStudentName);    assertEquals("Jane Doe", student.getName()); } 

The first step is to eliminate the unnecessary local variables studentName and secondStudentName. They don't add anything to the understanding of the method and can be replaced with simple queries to the student object, as in the very last assertEquals.

When you are done making this change, recompile and rerun your test in JUnit to ensure you haven't broken anything. Your code should look like this:

 public void testCreate() {    Student student = new Student("Jane Doe");    assertEquals("Jane Doe", student.getName());    Student secondStudent = new Student("Joe Blow");    assertEquals("Joe Blow", secondStudent.getName());    assertEquals("Jane Doe", student.getName()); } 

The second step: It is considered poor programming practice to embed String literals throughout your code. One reason is that the code can be difficult to follow if it is not clear what each String literal represents.

In this example, you also are breaking the rule against code duplication. Each of the two String literals appears twice within the test method. If you change one String, you have to change the other. This is more work, and it also means there is a possibility that you will introduce a defect in your code by changing one and not the other.

One way to eliminate this redundancy (and add a bit of expressiveness to the code) is to replace the String literals with String constants.

 final String firstStudentName = "Jane Doe"; 

This statement creates a reference named firstStudentName of the type String and assigns it an initial value of the String literal "Jane Doe".

The keyword final at the beginning of the statement indicates that the String reference cannot be changedno other object can be assigned to the reference. You are never required to specify final, but it is considered good form and helps document the intent that firstStudentName is acting as a constant. You will learn other uses for final later.

Now that you have declared the constant, you can replace the String literals with it.

 final String firstStudentName = "Jane Doe"; Student student = new Student(firstStudentName); assertEquals(firstStudentName, student.getName()); ... assertEquals(firstStudentName, student.getName()); 

Compile and rerun your test to ensure that you haven't inadvertently broken anything.

Apply a similar refactoring to the other String literal. In addition, change the local variable name student to firstStudent in order to be consistent with the variable name secondStudent. Between each small change, compile and use JUnit to ensure that you haven't broken anything. When finished, your code should look like this:

 public void testCreate() {    final String firstStudentName = "Jane Doe";    Student firstStudent = new Student(firstStudentName);    assertEquals(firstStudentName, firstStudent.getName());    final String secondStudentName = "Joe Blow";    Student secondStudent = new Student(secondStudentName);    assertEquals(secondStudentName, secondStudent.getName());    assertEquals(firstStudentName, firstStudent.getName()); } 

The final assertEquals proves your understanding of the way Java works rather than the functionality that you are trying to build. You would not likely keep this assertion around in a production system. You may choose to keep it or delete it. If you delete it, make sure you recompile and rerun your test!

Your development cycle is now:

  • Write a small test to assert some piece of functionality.

  • Demonstrate that the test fails.

  • Write a small bit of code to make this test pass.

  • Refactor both the test and code, eliminating duplicate concepts and ensuring that the code is expressive.

This cycle will quickly become an ingrained, natural flow of development.



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

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