Why can't programmers... design software?
There is a followup to this post. We need to talk about FizzBuzz.
/**
* Use to convert numbers to fizz/buzz representations
*/
public final class FizzBuzzOperator
{
private final FizzBuzzStrategy fizzbuzzStrategy;
/**
* @param fizzbuzzStrategy the strategy to use to find the
* {@link FizzBuzzOperation}
*/
private FizzBuzzOperator(FizzBuzzStrategy fizzbuzzStrategy) {
this.fizzbuzzStrategy = fizzbuzzStrategy;
}
/**
* Returns the fizz/buzz representation of the given number
*
* @param number the number to use
*/
public String op(int number){
return this.fizzbuzzStrategy.fizzBuzzOf(number).toString();
}
}
First time I found out about FizzBuzz was when I came across Jeff Atwood’s blog post Why Can’t Programmers.. Program?. As you would expect (since CodingHorror draws a lot of developers) answers begun to spur on how you can go on implementing it. On a closer look however you will notice that most (if not all) of those answers are on the basis of a switch/if statement and then try to apply the most obscure “optimisation” to come up with fewer lines and less characters failling to see the real issue. [Disclaimer]
What if I told you that the answer to the FizzBuzz algorithm is only one line of code without any if and it looks like that
public String op(int number){
return fizzBuzzOf(number).toString();
}
Not surprisingly it starts out with a single interface
public interface FizzBuzzOperation
{
public boolean applies(int number);
public String toString();
}
With 3 enums
public enum ModFizzBuzz implements FizzBuzzOperation
{
FizzBuzz(15),
Buzz(5),
Fizz(3);
private final int mod;
private ModFizzBuzz(int mod)
{
this.mod = mod;
}
public boolean applies(int number) {
return number % this.mod == 0;
}
}
And that’s all. Really. What’s left out is what strategy you need to use to find the corresponding FizzBuzzOperation. So here is one:
public static final FizzBuzzStrategy DEFAULT = new FizzBuzzStrategy()
{
private List<ModFizzBuzz> fizzbuzzes =
Lists.newArrayList(
ModFizzBuzz.FizzBuzz,
ModFizzBuzz.Buzz,
ModFizzBuzz.Fizz);
@Override
public FizzBuzzOperation fizzBuzzOf(final int number)
{
for (FizzBuzzOperation fizzbuzz : this.fizzbuzzes) {
if(fizzbuzz.applies(number))
return fizzbuzz;
}
/*
* Default FizzBuzzOperation - if none applies -
* returns the number as a String
*/
return new FizzBuzzOperation()
{
@Override
public boolean applies(int number){
return true;
}
@Override
public String toString() {
return String.valueOf(number);
}
};
}
};
So here a new challenge. Find the most efficient FizzBuzzStrategy and get a postcard from Edinburgh as well as a budge(1) on truly mastering FizzBuzz.
Here is the interface you need to implement
/**
* Implementations should provide an algorithm to find a
* {@link FizzBuzzOperation} for a number
*/
public interface FizzBuzzStrategy
{
/**
* Given a number we need to find the corresponding
* {@link FizzBuzzOperation}
* @param number an arbitary number
* @return the {@link FizzBuzzOperation} to apply
*/
public FizzBuzzOperation fizzBuzzOf(int number);
}
As with every software engineering problem you don’t really see the big picture if you are always in “low level” code. Another reason why you need to think in terms of objects
Note: There is also a “FizzBuzz” post that can be used to post solutions in the forrst started by @acrookston which got my attention and decided to write this post
Disclaimer: Jeff was really trying to make a point about writing an algorithm not doing software design, which is a personal view.
1: Wish there was a way to award budges @kyle