Lead Developer FizzBuzz
In part 1 we went over a simple implementation of FizzBuzz. Now we are going to advance to what I expect a lead developer would create.
Job titles are tricky things. I am not equating this with a job title, what I mean by a lead developer is that they could help a more junior developer develop a more complete solution.
Unit Tests
The first change is the addition of unit tests. Developers will develop structurally different code if they are required to develop unit tests. In order to test code correctly, you have to be able to expose the different facets of your creation. This allows unit tests to be short and robust.
package dev.boundary.waters.FizzBuzz;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class FizzBuzzTest {
@Test
void test1() {
FizzBuzz fb = new FizzBuzz();
assertEquals("1", fb.process(1), "failed for 1");
}
@Test
void test3() {
FizzBuzz fb = new FizzBuzz();
assertEquals("Fizz", fb.process(3), "failed for 3");
}
@Test
void test5() {
FizzBuzz fb = new FizzBuzz();
assertEquals("Buzz", fb.process(5), "failed for 5");
}
@Test
void test15() {
FizzBuzz fb = new FizzBuzz();
assertEquals("FizzBuzz", fb.process(15), "failed for 15");
}
@Test
void test0() {
FizzBuzz fb = new FizzBuzz();
try {
fb.process(0);
} catch (Exception e) {
return; // success
}
fail("Didn't get an exception for less than 1");
}
@Test
void test101() {
FizzBuzz fb = new FizzBuzz();
try {
fb.process(101);
} catch (Exception e) {
return; // success
}
fail("Didn't get an exception for less than 1");
}
@Test
void testEmpty() {
FizzBuzz fb = new FizzBuzz();
try {
fb.fizzBuzz(0, 0, System.out::println);
} catch (Exception e) {
return; // success
}
fail("Didn't get an exception for empty range");
}
@Test
void testOutOfOrder() {
FizzBuzz fb = new FizzBuzz();
try {
fb.fizzBuzz(101, 1, System.out::println);
} catch (Exception e) {
return; // success
}
fail("Didn't get an exception for empty range");
}
}
Factor out the hard coded input
The fizzBuzz method now takes the input parameters and output destination as parameters, allowing for tests as well as preparing for future changes:
public void fizzBuzz(int start, int finish,
Consumer<String> func) {
if(start >= finish) {
throw new IllegalArgumentException("start "
+ start + " must be less than finish "
+ finish);
}
IntStream.range(start, finish).mapToObj(
n -> process(n)).forEach(
s -> func.accept (s));
}
Optional: convert to Java streams
The advantage of changing to Java streams is that you can't put an if, break, return or other flow control statement into the stream by mistake. You also set yourself up for the ability to parallel process with a thread pool, etc.
The downside to this change is that it is quite a bit harder to debug (with a debugger) and it may not be common in a lot of teams to use Java streams. Both are valid reasons to not make this change.
Comments
Post a Comment