Write good Java code through code refactoring and unit testing

Writing clean and simple Java code is very important for test automation.

Because the Java code should not just work.

Other people should understand what it does and be able to make changes to it without a lot of effort.



Writing code that works is easy. 
Writing code that is easy to understand and maintain is difficult.

Assume that you have a bunch of lines of code that implement a specific problem.

The code makes sense to you because you wrote it.

But it is not clear at all for other people.

And it is quite difficult to change.


How do you write code that works, is clean, easy to understand and maintain? 


You can do it with code refactoring and unit testing.

Code refactoring means that, as soon as you have working code, you make changes to the code to simplify it.

The code refactoring changes may mean
  • breaking methods into more methods
  • simplifying how the code works
  • eliminating variables
  • reducing the number of parameters for a method
  • using an object instead of a few variables
  • creating new classes
  • merging methods
  • moving code from one class to another
  • changing variables and methods' names
  • and much more

These code changes may impact how the existing code works.

To ensure that the new changes are safe, we write unit tests that verify the functionality of existing code.

Then, after each future code change. we run the unit tests.

If the unit tests pass, the new code changes are safe.
If the unit tests fail, the new code changes are not good yet.

These 2 phases (code refactoring and executing unit tests) may need to be done repeatedly until the code becomes clean and easy to understand.


Code Refactoring Example


To understand how this process works, we will use the BINARY GAP exercise given sometimes in test automation job interviews.


BINARY GAP

Find longest sequence of zeros in binary representation of an integer.
A binary gap within a positive integer N is any maximal sequence of consecutive zeros that is surrounded by ones at both ends in the binary representation of N.
For example,
  • 9 has binary representation 1001 and contains a binary gap of length 2 
  • 529 has binary representation 1000010001 and contains two binary gaps: one of length 4 and one of length 3
  • 20 has binary representation 10100 and contains one binary gap of length 1
  • 15 has binary representation 1111 and has no binary gaps 
Write code that, given a positive integer N, returns the length of its longest binary gap.
The function should return 0 if N doesn't contain a binary gap.
For example, given N = 1041, the function should return 5, because N has binary representation 10000010001 and so its longest binary gap is of length 5.
Assume that N is an integer within the range [1..2,147,483,647].

Multiple iterations will be needed for this exercise, each iteration adding more changes to the results of the previous iteration.


Decompose the problem in smaller pieces


We need to find the binary gap for a binary representation of a number.

The binary representation of a number will be determined first.

Next, we will look into a way of verifying that the binary representation is correct.

Last, we will work on determining the binary gap for the binary representation.



Calculate the binary representation of a number


Each positive number has a binary representation.

For example,
  • The number 9 has binary representation 1001 
  • The number 529 has binary representation 1000010001 
  • The number 20 has binary representation 10100 
  • The number 15 has binary representation 1111 
The algorithm that creates the binary representation of a number is below:

1. start with 2 variables:

Integer number;
List list; 

2. the number variable is the value to be converted in a binary representation

3. the list is initially empty and will store the binary representation of the number

4. divide the number by 2 and get the quotient and the remainder:

quotient = number / 2; 
remainder = number % 2;

5. add the remainder to the end of a list

6. store the quotient value in the number variable

7. repeat from step 4 until number becomes 0

Example

100 / 2 = 50 (quotient), 0 (remainder)
list = 0

50 / 2 = 25 (quotient),0 (remainder)
list = 0, 0

25 / 2 = 12 (quotient),1 (remainder)
list = 0, 0, 1 

12 / 2 = 6 (quotient),0 (remainder)
list = 0, 0, 1, 0 

6 / 2 = 3 (quotient),0 (remainder)
list = 0, 0, 1, 0, 0 

3 / 2 = 1 (quotient),1 (remainder)
list = 0, 0, 1, 0, 0, 1 

1 / 2 = 0 (quotient),1 (remainder)
list = 0, 0, 1, 0, 0, 1, 1 

The list of remainders is 0, 0, 1, 0, 0, 1, 1.

This is the reversed binary value.

The binary value of the number is 1, 1, 0, 0, 1, 0, 0.

How does the code look like so far?

We write the code initially in the static void main() method of a standalone Java app.


 public static void main(String[] args) {    
  
  int initialNumber = 100; 
  List< integer > reversedList = new ArrayList< integer >();
  
  int tempNumber = initialNumber;
  
  while (tempNumber != 0) { 
   int remainder = tempNumber % 2;
   int divisionResult = tempNumber / 2;
   
   reversedList.add(remainder);   
   tempNumber = divisionResult;
  }
       
  System.out.println(initialNumber);
  System.out.println(reversedList);    
    
 } 


Reverse the binary list


The list that we have so far is in reverse order.

To reverse it, we can use the following algorithm:

1. start with 2 list variables:
  • the reversed binary list that we already have 
  • an empty list where we will add the elements of the binary list in reversed order 
2. start from the last element of the reversed binary list

3. go through each element of the reversed binary list from the last one to the first one

4. add each element of the reversed binary list to the end of the binary list


Example

list = 0, 0, 1, 0, 0, 1, 1

reversed list

1,
1, 1,
1, 1, 0,
1, 1, 0, 0,
1, 1, 0, 0, 1,
1, 1, 0, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0


public static void main(String[] args) {    
  
  int initialNumber = 100;
 
  List< integer > reversedList = new ArrayList< integer >();
  int tempNumber = initialNumber;
  
  while (tempNumber != 0) { 
   int remainder = tempNumber % 2;
   int divisionResult = tempNumber / 2;
   
   reversedList.add(remainder);   
   tempNumber = divisionResult;
  }
       
  System.out.println(initialNumber);
  System.out.println(reversedList);
     
  List< integer > binaryValue = new ArrayList< integer >();
  for (int i = reversedList.size() - 1; i >= 0; i--)
   binaryValue.add(reversedList.get(i));
  
  System.out.println(binaryValue);
    
 }     



Validate that the binary list is correct


We can generate the binary list for a number.

Butwe need a way to validate it as correct.

The following algorithm does the reversed operation and converts a binary list of 0 and 1 to a number:

binary value = 1, 1, 0, 0, 1, 0, 0

there are 7 digits.

number = 
1 * (2*2*2*2*2*2) + 
1 * (2*2*2*2*2) + 
0 * (2*2*2*2) + 
0 * (2*2*2) + 
1 * (2*2) + 
0 * (2) + 
0 * (1)
= 64 + 1 * 32 + 1 * 4 
= 64 + 32 + 4 
= 100

this can also be written using the pow Java function (pow(2, 3) = 2*2*2):

number = 
1* pow(2, 6) + 
1* pow(2, 5) + 
0* pow(2, 4) + 
0* pow(2, 3) + 
1* pow(2, 2) + 
0* pow(2, 1) + 
0* pow(2, 0) 
= 1 * 64 + 1 * 32 + 1 * 4 
= 64 + 32 + 4 
= 100


public class MainClass {

 public static void main(String[] args) {     
  
  int initialNumber = 100;
 
  List< integer > reversedList = new ArrayList< integer >();
  int tempNumber = initialNumber;
  
  while (tempNumber != 0) { 
   int remainder = tempNumber % 2;
   int divisionResult = tempNumber / 2;
   
   reversedList.add(remainder);   
   tempNumber = divisionResult;
  }
       
  System.out.println(initialNumber);
  System.out.println(reversedList);
     
  List < integer > binaryValue = new ArrayList < integer >();
  for (int i = reversedList.size() - 1; i >= 0; i--)
   binaryValue.add(reversedList.get(i));
  
  System.out.println(binaryValue);
  
  int numberFromBinary = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (binaryValue.get(i) == 1)
    numberFromBinary += Math.pow(2, powNumber - i);
      
  System.out.println(numberFromBinary);
  
  if (initialNumber != numberFromBinary)
   System.out.println("the binary value is not calculated correctly");
  
 }
 
}

I have added at the end of the code a comparison between the initial number and the number generated from the binary value.

These numbers should be equal if the binary value is calculated correctly.


Change the code to use unit tests

So far, we do not have any unit tests for the code.

We will add more code to the project soon.

But before doing that, we will change the existing code into a unit test:

  
 @Test
 public void unitTestsBinaryValue()
 {
  int initialNumber = 100;
  
  List< integer > reversedList = new ArrayList< integer >();
  int tempNumber = initialNumber;
  
  while (tempNumber != 0) { 
   int remainder = tempNumber % 2;
   int divisionResult = tempNumber / 2;
   
   reversedList.add(remainder);   
   tempNumber = divisionResult;
  }
       
  System.out.println(initialNumber);
  System.out.println(reversedList);
  
   
  List< integer > binaryValue = new ArrayList< integer >();
  for (int i = reversedList.size() - 1; i >= 0; i--)
   binaryValue.add(reversedList.get(i));
  
  System.out.println(binaryValue);
      
  int numberFromBinary = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (binaryValue.get(i) == 1)
    numberFromBinary += Math.pow(2, powNumber - i);
      
  System.out.println(numberFromBinary);
  
  assertEquals("the binary value is not calculated correctly", 
      initialNumber, 
      numberFromBinary);    

 }

The only difference from the previous code is using an assertion instead of the if statement for verifying if the binary value is calculated correctly. 



Code refactoring (break the code into methods)


The unit test does too many things.

We will create new methods (in the test class) that
  • create the reversed binary list
  • reverse the initial list to get the binary value
  • convert the binary value to a number 
The unit test will use the new methods and make the assertion:

 
public List< integer > getReversedBinaryValue(int number)
 {
  List< integer > reversedValue = new ArrayList< integer >();
  int tempNumber = number;
  
  while (tempNumber != 0)
  { 
   int remainder = tempNumber % 2;
   int divisionResult = tempNumber / 2;
   
   reversedValue.add(remainder);   
   tempNumber = divisionResult;
  }
  
  return reversedValue;  
 }  
 

 public List< integer > reversedList(List< integer > list)
 {
  List< integer > reversedList = new ArrayList< integer >();
  
  for (int i = list.size() - 1; i >= 0; i--)
   reversedList.add(list.get(i));
  
  return reversedList;
 }  
 

 public int getIntFromBinary(List< integer > binaryValue)
 {
  int number = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (binaryValue.get(i) == 1)
    number += Math.pow(2, powNumber - i);
  
  return number;
 } 
 

 @Test
 public void unitTestsBinaryValue()
 {
  int number = 100;
  List< integer > reversedBinaryValue = getReversedBinaryValue(100);
  List< integer > binaryValue = reversedList(reversedBinaryValue);
  int numberFromBinary = getIntFromBinary(binaryValue);
  
  assertEquals("the 2 numbers are not equal", number, numberFromBinary);  
 }



Add more unit tests to validate the binary value


We need to validate the code that creates the binary value for more numeric values.

The code changes should be very easy to understand:

 
public List< integer > getReversedBinaryValue(int number)
 {
  List< integer > reversedValue = new ArrayList< integer >();
  int tempNumber = number;
  
  while (tempNumber != 0)
  { 
   int remainder = tempNumber % 2;
   int divisionResult = tempNumber / 2;
   
   reversedValue.add(remainder);   
   tempNumber = divisionResult;
  }
  
  return reversedValue;  
 }  
 

 public List< integer > reversedList(List< integer > list)
 {
  List< integer > reversedList = new ArrayList< integer >();
  
  for (int i = list.size() - 1; i >= 0; i--)
   reversedList.add(list.get(i));
  
  return reversedList;
 }  
 

 public int getIntFromBinary(List< integer > binaryValue)
 {
  int number = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (binaryValue.get(i) == 1)
    number += Math.pow(2, powNumber - i);
  
  return number;
 } 
 

 @Test
 public void unitTestsBinaryValue()
 {
  assertEquals(100, 
      getIntFromBinary(
       reversedList(
        getReversedBinaryValue(100))));
  
  assertEquals(1234567, 
      getIntFromBinary(
       reversedList(
        getReversedBinaryValue(1234567))));
  
  assertNotEquals(111, 
      getIntFromBinary(
       reversedList(
        getReversedBinaryValue(110))));
  
  assertEquals(1, 
      getIntFromBinary(
       reversedList(
        getReversedBinaryValue(1))));
  
  assertEquals(0, 
      getIntFromBinary(
       reversedList(
        getReversedBinaryValue(0))));
 }
  
}


Code refactoring
(optimize code, remove method, remove variables used once)

There are a few changes that can be done to the existing code:
  • remove variables that are used only once (remainder and divisionResult variables) 
  • in the getReversedBinaryValue() method, we can add the remainders to the beginning of the reversedList instead at the end; this way, the reversedList will be in the correct order so we dont need to reverse it 
  • since the method generates the correct binary value, the name can change from getReversedBinaryValue() to binaryValue()
  • we can remove the reversedList() method as it is no longer needed 
  • the name of the getIntFromBinary() can be changed to intFromBinary() 
  • we can update the unit tests by removing the calls to the reverseList() method

public class BinaryGapTestsInitial {  

 
 public List binaryValue(int number)
 {
  List binaryValue = new ArrayList();
  int tempNumber = number;
  
  while (tempNumber != 0)
  { 
   binaryValue.add(0, tempNumber % 2);   
   tempNumber = tempNumber / 2;
  }
  
  return binaryValue;  
 }
  

 public int intFromBinary(List binaryValue)
 {
  int number = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (binaryValue.get(i) == 1)
    number += Math.pow(2, powNumber - i);
  
  return number;
 }
  

 @Test
 public void unitTestsBinaryValue()
 {
  assertEquals(100,     intFromBinary(binaryValue(100)));  
  assertEquals(1234567, intFromBinary(binaryValue(1234567)));  
  assertNotEquals(111,  intFromBinary(binaryValue(110)));  
  assertEquals(1,       intFromBinary(binaryValue(1)));  
  assertEquals(0,       intFromBinary(binaryValue(0)));
 }
  

}


Add more code (calculate the longest 0 gap)


We can calculate the longest 0 gap in the following way:

binary value = 10100100010000 

for each digit of the binary value 

  if digit == 0 

    find the index of the first 1 that follows 

   if the index of the first 1 is greater than 0 

      calculate the 0 gap 
      (0 gap = difference between the 1 index and the index of the current 0) 
     
      if this 0 gap is the highest, 
          save it; 

     continue after the 1; 

   else 

      stop 



public class BinaryGapTestsInitial {  
 

 public List< integer > binaryValue(int number)
 {
  List< integer > binaryValue = new ArrayList< integer >();
  int tempNumber = number;
  
  while (tempNumber != 0)
  { 
   binaryValue.add(0, tempNumber % 2);   
   tempNumber = tempNumber / 2;
  }
  
  return binaryValue;  
 }
 
  
 public int intFromBinary(List< integer > binaryValue)
 {
  int number = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (binaryValue.get(i) == 1)
    number += Math.pow(2, powNumber - i);
  
  return number;
 }
 

 public int longest0Gap(List< integer > binaryValue)
 {
  int longestGap = 0;
  
  for (int i = 0; i< binaryValue.size(); i++)
   if (binaryValue.get(i) == 0)
   {    
    int indexOfNext1 = 0;
    int j;
    int gap = 0;
    
    //find the index of the next 1
    for (j = i + 1; j < binaryValue.size(); j++)
     if (binaryValue.get(j) == 1)
     {
      indexOfNext1 = j; 
      break;
     }
               
    //if there is a 1
    if (indexOfNext1 > 0)
    {
     gap = indexOfNext1 - i;
     
     //continue after the next 1
     i = indexOfNext1;
     
     //update the longest gap
     if (gap > longestGap)
      longestGap = gap;
    }
    else
     break;
                 
   }  
  
   return longestGap;   
 }
 

 @Test
 public void unitTestsBinaryValue()
 {
  assertEquals(100,     intFromBinary(binaryValue(100)));
  assertEquals(1234567, intFromBinary(binaryValue(1234567)));
  assertNotEquals(111,  intFromBinary(binaryValue(110)));
  assertEquals(1,       intFromBinary(binaryValue(1)));
  assertEquals(0,       intFromBinary(binaryValue(0)));
 }
 

 /*
    0  = 0       --> longest gap = 0
    1  = 1       --> longest gap = 0
    2  = 10      --> longest gap = 0
    3  = 11      --> longest gap = 0
    5  = 101     --> longest gap = 1
    20 = 10100   --> longest gap = 2
    32 = 100000  --> longest gap = 0
    41 = 101001  --> longest gap = 2
 */
 @Test
 public void unitTestsLongest0Gap()
 {
  assertEquals(0, longest0Gap(binaryValue(0)));  
  assertEquals(0, longest0Gap(binaryValue(1)));  
  assertEquals(0, longest0Gap(binaryValue(2)));  
  assertEquals(0, longest0Gap(binaryValue(3)));  
  assertEquals(1, longest0Gap(binaryValue(5)));
  assertEquals(1, longest0Gap(binaryValue(20)));
  assertEquals(0, longest0Gap(binaryValue(32)));
  assertEquals(2, longest0Gap(binaryValue(41)));  
 }

}



Code refactoring (move code to new methods)


There are multiple possible code changes :
  • move the code that calculates the index of the next 1 to a separate method 
  • create methods that check if a digit is equal to 0 or 1 
The existing unit tests help verifying if the new code changes are good.


public class BinaryGapTestsInitial {  
 

 public List< integer > binaryValue(int number)
 {
  List< integer > binaryValue = new ArrayList< integer >();
  int tempNumber = number;
  
  while (!isEqualTo0(tempNumber))
  { 
   binaryValue.add(0, tempNumber % 2);   
   tempNumber = tempNumber / 2;
  }
  
  return binaryValue;  
 }
   

 public int intFromBinary(List< integer > binaryValue)
 {
  int number = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (isEqualTo1(binaryValue.get(i)))
    number += Math.pow(2, powNumber - i);
  
  return number;
 }
  

 public int longest0Gap(List< integer > binaryValue)
 {
  int longestGap = 0;
  
  for (int i = 0; i< binaryValue.size(); i++)       
   if (isEqualTo0(binaryValue.get(i)))
   { 
    int first0Index = i;
    int indexOf1 = next1Index(first0Index, binaryValue);   
    
    //if there is a 1
    if (indexOf1 > 0)
    {
     int gap = indexOf1 - i;
     
     //continue after the next 1
     i = indexOf1;
     
     //update the longest gap
     if (gap > longestGap)
      longestGap = gap;
    }
    else
     break;
       
   }  
  
   return longestGap;   
 } 
  

 private int next1Index(int i, List< integer > binaryValue)
 {  
  int index = 0;
  
  for (int j = i + 1; j < binaryValue.size(); j++)
   if (isEqualTo1(binaryValue.get(j)))      
   {
    index = j;    
    break;
   }
  
  return index;
 }
 

 private Boolean isEqualTo0(Integer number)
 {
  return number == 0;
 }

 
 private Boolean isEqualTo1(Integer number)
 {
  return number == 1;
 }

 
 @Test
 public void unitTestsBinaryValue()
 {
  assertEquals(100,     intFromBinary(binaryValue(100)));
  assertEquals(1234567, intFromBinary(binaryValue(1234567)));
  assertNotEquals(111,  intFromBinary(binaryValue(110)));
  assertEquals(1,       intFromBinary(binaryValue(1)));
  assertEquals(0,       intFromBinary(binaryValue(0)));
 }

  
 @Test
 public void unitTestsLongest0Gap()
 {
  assertEquals(0, longest0Gap(binaryValue(0)));  
  assertEquals(0, longest0Gap(binaryValue(1)));  
  assertEquals(0, longest0Gap(binaryValue(2)));  
  assertEquals(0, longest0Gap(binaryValue(3)));  
  assertEquals(1, longest0Gap(binaryValue(5)));
  assertEquals(1, longest0Gap(binaryValue(20)));
  assertEquals(0, longest0Gap(binaryValue(32)));
  assertEquals(2, longest0Gap(binaryValue(41)));  
 }

  
}



Code refactoring (move code to new method)

We can create a method that calculates the current gap.

The existing unit tests will help to check if the code changes are good:

public class BinaryGapTestsInitial {  

 
 public List< integer > binaryValue(int number)
 {
  List< integer > binaryValue = new ArrayList< integer >();
  int tempNumber = number;
  
  while (!isEqualTo0(tempNumber))
  { 
   binaryValue.add(0, tempNumber % 2);   
   tempNumber = tempNumber / 2;
  }
  
  return binaryValue;  
 }

   
 public int intFromBinary(List< integer > binaryValue)
 {
  int number = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (isEqualTo1(binaryValue.get(i)))
    number += Math.pow(2, powNumber - i);
  
  return number;
 }

  
 public int longest0Gap(List< integer > binaryValue)
 {
  int longestGap = 0;
  
  for (int i = 0; i< binaryValue.size(); i++)       
   if (isEqualTo0(binaryValue.get(i)))
   { 
    int first0Index = i;
    int indexOf1 = next1Index(first0Index, binaryValue);                
    if (indexOf1 > 0)
    {
     if (currentGap(indexOf1, first0Index) > longestGap)
      longestGap = currentGap(indexOf1, first0Index); 
    
     i = indexOf1;
    }
    else
     break;
   }  
  
   return longestGap;   
 }

 
 private Integer currentGap(Integer next1Index, Integer first0Index)
 {
  return next1Index - first0Index;
 }

  
 private int next1Index(int i, List< integer > binaryValue)
 {  
  int index = 0;
  
  for (int j = i + 1; j < binaryValue.size(); j++)
   if (isEqualTo1(binaryValue.get(j)))      
   {
    index = j;    
    break;
   }
  
  return index;
 }

 
 private Boolean isEqualTo0(Integer number)
 {
  return number == 0;
 }

 
 private Boolean isEqualTo1(Integer number)
 {
  return number == 1;
 }

 
 @Test
 public void unitTestsBinaryValue()
 {
  assertEquals(100,     intFromBinary(binaryValue(100)));
  assertEquals(1234567, intFromBinary(binaryValue(1234567)));  
  assertNotEquals(111,  intFromBinary(binaryValue(110)));
  assertEquals(1,       intFromBinary(binaryValue(1)));
  assertEquals(0,       intFromBinary(binaryValue(0)));
 }

  
 @Test
 public void unitTestsLongest0Gap()
 {
  assertEquals(0, longest0Gap(binaryValue(0)));  
  assertEquals(0, longest0Gap(binaryValue(1)));  
  assertEquals(0, longest0Gap(binaryValue(2)));  
  assertEquals(0, longest0Gap(binaryValue(3)));  
  assertEquals(1, longest0Gap(binaryValue(5)));
  assertEquals(1, longest0Gap(binaryValue(20)));
  assertEquals(0, longest0Gap(binaryValue(32)));
  assertEquals(2, longest0Gap(binaryValue(41)));  
 }

  
}


Code refactoring (make decisions in a different way)

We can also change the code by
  • moving the calculation of the next 1 index in the currentGap method 
  • making decisions depending on the gap instead of the next 1 index 
  • if gap is greater than longest gap, update the longest gap 
  • if gap > 0, update the index of the first for, otherwise end the first for

public class BinaryGapTestsInitial {  

 
 public List< integer > binaryValue(int number)
 {
  List< integer > binaryValue = new ArrayList< integer >();
  int tempNumber = number;
  
  while (!isEqualTo0(tempNumber))
  { 
   binaryValue.add(0, tempNumber % 2);   
   tempNumber = tempNumber / 2;
  }
  
  return binaryValue;  
 }

   
 public int intFromBinary(List< integer > binaryValue)
 {
  int number = 0;
  int powNumber = binaryValue.size() - 1;
  
  for (int i = 0; i < binaryValue.size(); i++)
   if (isEqualTo1(binaryValue.get(i)))
    number += Math.pow(2, powNumber - i);
  
  return number;
 }

 
 public int longest0Gap(List< integer > binaryValue)
 {
  int longestGap = 0;
  
  for (int index = 0; index < binaryValue.size(); index ++)       
   if (isEqualTo0(binaryValue.get(index)))
   {            
    int gap = currentGap(index, binaryValue);    
    longestGap = gap > longestGap ? gap : longestGap;           
    index      = gap > 0 ? index + gap: binaryValue.size();     
   }  
  
   return longestGap;   
 }

 
 private Integer currentGap(Integer indexOf0, List< integer > binaryValue)
 {
  int indexOf1 = next1Index(indexOf0, binaryValue);
  return indexOf1 > 0 ? indexOf1 - indexOf0 : 0;
 }

  
 private int next1Index(int i, List< integer > binaryValue)
 {  
  int index = 0;
  
  for (int j = i + 1; j < binaryValue.size(); j++)
   if (isEqualTo1(binaryValue.get(j)))      
   {
    index = j;    
    break;
   }
  
  return index;
 }

 
 private Boolean isEqualTo0(Integer number)
 {
  return number == 0;
 }

 
 private Boolean isEqualTo1(Integer number)
 {
  return number == 1;
 }

 
 @Ignore
 public void unitTestsBinaryValue()
 {
  assertEquals(100,     intFromBinary(binaryValue(100)));
  assertEquals(1234567, intFromBinary(binaryValue(1234567)));
  assertNotEquals(111,  intFromBinary(binaryValue(110)));
  assertEquals(1,       intFromBinary(binaryValue(1)));
  assertEquals(0,       intFromBinary(binaryValue(0)));
 }

 
 @Test
 public void unitTestsLongest0Gap()
 {
  assertEquals(0, longest0Gap(binaryValue(0)));  
  assertEquals(0, longest0Gap(binaryValue(1)));  
  assertEquals(0, longest0Gap(binaryValue(2)));  
  assertEquals(0, longest0Gap(binaryValue(3)));  
  assertEquals(1, longest0Gap(binaryValue(5)));
  assertEquals(1, longest0Gap(binaryValue(20)));
  assertEquals(0, longest0Gap(binaryValue(32)));
  assertEquals(2, longest0Gap(binaryValue(41)));  
 }


}


Create a new class for the binary list

So far, we have in the test class unit tests and other methods that deal with binary lists.

We can move all code related to the binary lists in a new class.

public class BinaryList {

 
 Integer number;
 List< integer > binaryValue = new ArrayList< integer >();
 int digitsCount;
 

 public BinaryList(Integer number) {
  this.number = number;
  calculateBinaryValue(number);    
 }

 
 private int digitsCount() {
  return this.digitsCount;
 } 

 
 private void calculateBinaryValue(int number) {    
  int tempNumber = number;  
  while (tempNumber > 0) { 
   this.binaryValue.add(0, tempNumber % 2);   
   tempNumber = tempNumber / 2;
  }    
  this.digitsCount = binaryValue.size();
 } 

 
 public int intFromBinary()
 {
  int number = 0;
  int powNumber = digitsCount() - 1;  
  for (int i = 0; i < digitsCount(); i++)
   if (isDigitEqualTo1(i))
    number += Math.pow(2, powNumber - i);  
  return number;
 }

 
 private Boolean isDigitEqualTo0(Integer index)
 {
  return binaryValue.get(index) == 0;
 }
 

 private Boolean isDigitEqualTo1(Integer index)
 {
  return binaryValue.get(index) == 1;
 }
 

 public int longest0Gap()
 {    
  int longestGap = 0;  
  for (int index = 0; index < digitsCount(); index ++)       
   if (isDigitEqualTo0(index)) {            
    int gap    = currentGap(index);     
    longestGap=gap>longestGap?gap:longestGap;     
    index = gap > 0 ? index + gap: index;     
   }  
   return longestGap;   
 }
 

 private Integer currentGap(Integer indexOf0)
 {
  int indexOf1 = next1Index(indexOf0);
  return indexOf1 > 0 ? indexOf1 - indexOf0 : 0;
 }
  

 private int next1Index(int current0Index)
 {  
  int index = current0Index;
  for (; index < digitsCount(); index++)
   if (isDigitEqualTo1(index)) 
    break;     
  return index < digitsCount() ? index : 0;
 }


}


The class has just 2 public methods:
  • one that calculates the longest 0 gap 
  • one that calculates the number that corresponds to the binary value 
The class uses a List field for the binary value and a int field for the size of the list. 

The test class becomes very simple once the binary value code is moved to the new class:



public class BinaryGapTestsInitial {  
 
 @Test
 public void unitTestsBinaryValue()
 {
  assertEquals(100,     new BinaryList(100).intFromBinary());
  assertEquals(1234567, new BinaryList(1234567).intFromBinary());
  assertNotEquals(111,  new BinaryList(110).intFromBinary());
  assertEquals(1,       new BinaryList(1).intFromBinary());
  assertEquals(0,       new BinaryList(0).intFromBinary());
 }
 
 @Test
 public void unitTestsLongest0Gap()
 {  
  assertEquals(0, new BinaryList(0).longest0Gap());  
  assertEquals(0, new BinaryList(1).longest0Gap());  
  assertEquals(0, new BinaryList(2).longest0Gap());  
  assertEquals(0, new BinaryList(3).longest0Gap());  
  assertEquals(1, new BinaryList(5).longest0Gap());
  assertEquals(1, new BinaryList(20).longest0Gap());
  assertEquals(0, new BinaryList(32).longest0Gap());
  assertEquals(2, new BinaryList(41).longest0Gap());  
 }
  
}


I hope that you found this small project interesting and useful.

A short summary of the code refactorings done:

  • extracted new methods from existing ones
  • removing variables used only once
  • removing methods that are no longer needed
  • using good names for methods and variables
  • moving code to a new class


3 Responses to "Write good Java code through code refactoring and unit testing"

  1. Interesting. Thanks very much.

    ReplyDelete
  2. Excellent article and most helpful content... I really like your blog. Thank a lot for sharing your knowledge. Keep posting wonderful articles on testing :)

    ReplyDelete