More actions
imported>Unknown No edit summary |
(Repair batch-0001 pages from live compare) |
||
| Line 18: | Line 18: | ||
LinkedList result = new LinkedList(); | LinkedList result = new LinkedList(); | ||
int | int [] primes = generatePrimes(maxValue); | ||
for(int i = 0; i <primes.length; ++i) | for(int i = 0; i <primes.length; ++i) | ||
{ | { | ||
result.add(new Integer(primes | result.add(new Integer(primes[i])); | ||
} | } | ||
| Line 29: | Line 29: | ||
*/ | */ | ||
public static int | public static int [] generatePrimes(int maxValue) | ||
{ | { | ||
if(maxValue >= 2) // the only valid case | if(maxValue >= 2) // the only valid case | ||
| Line 35: | Line 35: | ||
// declarations | // declarations | ||
int s = maxValue+1; // size of array | int s = maxValue+1; // size of array | ||
boolean | boolean[] f = new boolean[s]; | ||
int i; | int i; | ||
// initialize array to true. | // initialize array to true. | ||
for(i=0; i<s; i+) | for(i=0; i<s; i+) | ||
f | f[i] = true; | ||
// get rid of known non-primes | // get rid of known non-primes | ||
f | f[0] = f[1] = false; | ||
// sieve | // sieve | ||
| Line 50: | Line 50: | ||
{ | { | ||
for(j=2*1; j<s; j+=i) | for(j=2*1; j<s; j+=i) | ||
for | for[j]=false; // multiple is not prime | ||
} | } | ||
| Line 56: | Line 56: | ||
int count = 0; | int count = 0; | ||
for(i=0; i<s; i++) | for(i=0; i<s; i++) | ||
if(f | if(f[i]) | ||
count++;; // bump count. | count++;; // bump count. | ||
int | int [] primes = new int[count]; | ||
// move the primes into the result | // move the primes into the result | ||
for(i=0, j=0; i<s; i++) | for(i=0, j=0; i<s; i++) | ||
{ | { | ||
if(f | if(f[i]) // if prime | ||
{ | { | ||
primes | primes[j++] = i; | ||
} | } | ||
} | } | ||
| Line 74: | Line 74: | ||
} // maxValue >= 2 | } // maxValue >= 2 | ||
else | else | ||
return new int | return new int[0]; return null array if bad input. | ||
} | } | ||
} | } | ||
| Line 86: | Line 86: | ||
public class TestGeneratePrimes extends TestCase | public class TestGeneratePrimes extends TestCase | ||
{ | { | ||
private int | private int[] m_primes = new int[] | ||
{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 }; | { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 }; | ||
| Line 96: | Line 96: | ||
public void testPrime() | public void testPrime() | ||
{ | { | ||
int | int [] centArray = GeneratePrimes.generatePrimes(100); | ||
assertEquals(centArray.length, 25); | assertEquals(centArray.length, 25); | ||
assertEquals(centArray | assertEquals(centArray[24], 97); | ||
} | } | ||
| Line 111: | Line 111: | ||
{ | { | ||
int bound = 1000; | int bound = 1000; | ||
int | int [] primes = GeneratePrimes.getneratePrimes(bound); | ||
for(int it = 0; it < primes.length; ++it) | for(int it = 0; it < primes.length; ++it) | ||
{ | { | ||
int n = primes | int n = primes[it]; | ||
assert("is prime", isPrime(n)); | assert("is prime", isPrime(n)); | ||
} | } | ||
| Line 149: | Line 149: | ||
public void testBasic() | public void testBasic() | ||
{ | { | ||
int | int [] primes = GeneratePrimes.generatePrimes(m_primes[m_primes.length - 1]); | ||
assertEquals(m_primes.length, primes.length); | assertEquals(m_primes.length, primes.length); | ||
for(int i = 0; i < primes.length; ++i) | for(int i = 0; i < primes.length; ++i) | ||
{ | { | ||
int intValue = primes | int intValue = primes[i]; | ||
assertEquals(intValue, m_primes | assertEquals(intValue, m_primes[i]); | ||
} | } | ||
public void testListBasic() | public void testListBasic() | ||
{ | { | ||
List primes = GeneratePrimes.primes(m_primes | List primes = GeneratePrimes.primes(m_primes[m_primes.length -1]); | ||
assertEquals(m_primes.length, primes.size()); | assertEquals(m_primes.length, primes.size()); | ||
Iterator index = primes.iterator(); | Iterator index = primes.iterator(); | ||
| Line 166: | Line 166: | ||
Integer value = (Integer)index.next(); | Integer value = (Integer)index.next(); | ||
int intValue = value.intValue(); | int intValue = value.intValue(); | ||
assertEquals(intValue, m_primes | assertEquals(intValue, m_primes[i]); | ||
} | } | ||
} | } | ||
| Line 172: | Line 172: | ||
public void testSingle() | public void testSingle() | ||
{ | { | ||
int | int [] primes = GeneratePrimes.generatePrimes(2); | ||
assertEquals(1, primes.length); | assertEquals(1, primes.length); | ||
} | } | ||
| Line 185: | Line 185: | ||
public void testZero() | public void testZero() | ||
{ | { | ||
int | int [] primes = GeneratePrimes.generatePrimes(0); | ||
assertEquals(0, primes,length); | assertEquals(0, primes,length); | ||
} | } | ||
| Line 211: | Line 211: | ||
} | } | ||
static boolean contains(int value, int | static boolean contains(int value, int [] primes) | ||
{ | { | ||
if(primes.length == 0) return false; | if(primes.length == 0) return false; | ||
| Line 217: | Line 217: | ||
for(int i = 0; i < primes.length; ++i) | for(int i = 0; i < primes.length; ++i) | ||
{ | { | ||
if(value == primes | if(value == primes[i]) return true; | ||
} | } | ||
| Line 226: | Line 226: | ||
Refactor Tests First | Refactor Tests First | ||
Our first activity is to refactor the tests. | Our first activity is to refactor the tests. We might need some justification정당화 for refactoring the tests first, I was thinking that it should tie동여매다 into writing the tests first The array method is being removed so all the test cases for this method need to be removed as well. Looking at Listing2. "Class TestsGeneratePrimes," it appears that the only difference in the suite of tests is that one tests a List of Integers and the other tests an array of ints. For example, testListPrime and testPrime test the same thing, the only difference is that testListPrime expects a List of Integer objects and testPrime expects and array of int variables. Therefore, we can remove the tests that use the array method and not worry that they are testing something that is different than the List tests. | ||
Latest revision as of 23:56, 26 March 2026
Baby Steps, Safely
Kent Beck and James Newkirk
Introduction
This article outlines the refactoring of an algorithm that generate the prime numbers up to a user specified maximum. This algorithm is called the Sieve of Eratosthenes. This article demonstrates that the granularity of the changes made to the source code are very small and rely completely on the ability to recompile and test the code after every change no matter how small. The step where the code is tested insures that each step is done safely. It is important to note that the execution of tests do not actually guarantee that the code is correct. The execution of the tests just guarantees that it isn't any worse that it used to db, prior to the change. This is good enough for the purposes of refactoring since we are tring to not damage anything thay may have worked before Therefore for each change in the code we will be recompilling the code and running the tests.
The code that is to be refactored has existed in the system for awhile. It has undergone a couple of transformations. Initially it returned an array of int variables that are the prime numbers. When the new collection library was introduced in Java2 the interface was changed to return a List of Integer objects. Going forward the method that returns a List is the preferred method, so the method that returns an array has been marked as being deprecated for the last couple of releases. During this release the array member function will be removed. Listing1, "Class GeneratePrimes," contains the source code for both methods.
The algorithm is quite simple. Given an array of integers starting at 2.Cross out all multiples of 2. Find the next uncrossed integer, and cross out all of its multiples. Repeat until you have passed the square root of the maximum value. This algorithm is implemented in the method generatePrimes() in Listing1. "Class GeneratePrimes,".
inport java.util.*;
pulbic class GeneratePrimes
{
public static List primes(int maxValue)
{
LinkedList result = new LinkedList();
int [] primes = generatePrimes(maxValue);
for(int i = 0; i <primes.length; ++i)
{
result.add(new Integer(primes[i]));
}
return result;
}
/** @param maxValue is the generation limit.
*/
public static int [] generatePrimes(int maxValue)
{
if(maxValue >= 2) // the only valid case
{
// declarations
int s = maxValue+1; // size of array
boolean[] f = new boolean[s];
int i;
// initialize array to true.
for(i=0; i<s; i+)
f[i] = true;
// get rid of known non-primes
f[0] = f[1] = false;
// sieve
int j;
for(i=2; i<Math.sqrt(s)+1; i++)
{
for(j=2*1; j<s; j+=i)
for[j]=false; // multiple is not prime
}
// how many primes are there?
int count = 0;
for(i=0; i<s; i++)
if(f[i])
count++;; // bump count.
int [] primes = new int[count];
// move the primes into the result
for(i=0, j=0; i<s; i++)
{
if(f[i]) // if prime
{
primes[j++] = i;
}
}
// return the primes
return primes;
} // maxValue >= 2
else
return new int[0]; return null array if bad input.
}
}
The test cases for the GeneratePrimes class are implemented using the JUnit testing framework. The tests are contained in class called TestGeneratePrames. There are a 5 tests for each return type (array and List), which appear to be similiar. Our first step to insure보증하다, 책임지다 that we are starting from a stable base is to make sure what we have works. Therefore, recompile the both GeneratePrimes and TestGeneratePrime and run the tests.
Class TestsGeneratePrimes
import junit.framework.*;
import java.util.*;
public class TestGeneratePrimes extends TestCase
{
private int[] m_primes = new int[]
{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
public TestGeneratePrimes(String name)
{
super(name);
}
public void testPrime()
{
int [] centArray = GeneratePrimes.generatePrimes(100);
assertEquals(centArray.length, 25);
assertEquals(centArray[24], 97);
}
public void testListPrime()
{
List centList = GeneratePrimes.primes(100);
assertEquals(25, centList.size());
assertEquals(centList.get(24), new Integer(97));
}
public void testLots()
{
int bound = 1000;
int [] primes = GeneratePrimes.getneratePrimes(bound);
for(int it = 0; it < primes.length; ++it)
{
int n = primes[it];
assert("is prime", isPrime(n));
}
for(int i = 1; i <= bound; i++)
{
if(isPrime(i))
assert("contains primes", contains(i, primes));
else
assert("doesn't contain composites", !contains(i, primes));
}
}
public void testListLots()
{
int bound = 10101;
List primes = GeneratePrimes.primes(bound);
Iterator it = primes.iterator();
while(it.hasNext())
{
int n = ((Integer)it.next()).intValue();
assert("is prime", isPrime(n));
}
for(int i = 1; i <= bound; i++)
{
if(isPrime(i))
assert("contains primes", primes.contains(new Integer(i)));
else
assert("doesn't contain composites", !primes.contains(new Integer(i)));
}
}
public void testBasic()
{
int [] primes = GeneratePrimes.generatePrimes(m_primes[m_primes.length - 1]);
assertEquals(m_primes.length, primes.length);
for(int i = 0; i < primes.length; ++i)
{
int intValue = primes[i];
assertEquals(intValue, m_primes[i]);
}
public void testListBasic()
{
List primes = GeneratePrimes.primes(m_primes[m_primes.length -1]);
assertEquals(m_primes.length, primes.size());
Iterator index = primes.iterator();
for(int i = 0; index.hasNext(); i++)
{
Integer value = (Integer)index.next();
int intValue = value.intValue();
assertEquals(intValue, m_primes[i]);
}
}
public void testSingle()
{
int [] primes = GeneratePrimes.generatePrimes(2);
assertEquals(1, primes.length);
}
public void testListSingle()
{
List primes = GeneratePrimes.primes(2);
assertEquals(1, primes.size());
assert(primes.contains(new Integer(2)));
}
public void testZero()
{
int [] primes = GeneratePrimes.generatePrimes(0);
assertEquals(0, primes,length);
}
public void testListZero()
{
List primes = GeneratePrimes.primes(0);
assertEquals(0, primes.size());
}
static boolean isPrime(int n)
{
if(n < 2 ) return false;
boolean result = true;
double x = Math.sqrt(n);
int i = 2;
while(result && i <= x)
{
result = (0 != n % i);
i += 1;
}
return result;
}
static boolean contains(int value, int [] primes)
{
if(primes.length == 0) return false;
for(int i = 0; i < primes.length; ++i)
{
if(value == primes[i]) return true;
}
return false;
}
}
}
Refactor Tests First Our first activity is to refactor the tests. We might need some justification정당화 for refactoring the tests first, I was thinking that it should tie동여매다 into writing the tests first The array method is being removed so all the test cases for this method need to be removed as well. Looking at Listing2. "Class TestsGeneratePrimes," it appears that the only difference in the suite of tests is that one tests a List of Integers and the other tests an array of ints. For example, testListPrime and testPrime test the same thing, the only difference is that testListPrime expects a List of Integer objects and testPrime expects and array of int variables. Therefore, we can remove the tests that use the array method and not worry that they are testing something that is different than the List tests.