An approach I didn’t use in my last post (well, I did, somewhat, with the enum method) is the object oriented approach. Of course, if we’re going to make it object oriented, we should strive to write reusable components for any part of it, and to use interfaces when possible. So here it is, in all its glory:
/*
* This code is written by W (aka Raymond Wold) and is released into the public domain.
* - It's not my code, I just wrote it.
*/
package com.w_wins.fizzbuzz;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public class FizzBuzz implements Runnable{
private final Facilitator<Integer, String> facilitator;
public FizzBuzz(){
final List<ConditionalAdaptor<Integer, String>> list=new ArrayList<ConditionalAdaptor<Integer, String>>();
final FizzBuzzConditionFactory fizzBuzzConditions=new FizzBuzzConditionFactory();
list.add(fizzBuzzConditions.generateFizzBuzzAdaptor());
list.add(fizzBuzzConditions.generateFizzAdaptor());
list.add(fizzBuzzConditions.generateBuzzAdaptor());
facilitator=new Facilitator<Integer, String>(list, new ToStringAdaptor<Integer>());
}
@Override
public void run(){
facilitator.runOn(new IntegerRangeGenerator(1, 100), new Outputter());
}
}
interface Adaptor<F, T>{
public T convert(F value);
}
interface Condition<V>{
public boolean matches(V value);
}
interface ConditionalAdaptor<F, T> extends Adaptor<F, T>,Condition<F>{
}
interface Handler<T>{
public void handle(T value);
}
class Facilitator<F, T>{
final List<ConditionalAdaptor<F, T>> conditionalAdaptors;
final Adaptor<F, T> finalAdaptor;
public Facilitator(final List<ConditionalAdaptor<F, T>> conditionalAdaptors, final Adaptor<F, T> finalAdaptor){
this.conditionalAdaptors=conditionalAdaptors;
this.finalAdaptor=finalAdaptor;
}
public void runOn(final Iterator<F> iterator, final Handler<T> handler){
while(iterator.hasNext())
handler.handle(runAdaptors(iterator.next()));
}
public T runAdaptors(final F value){
for(ConditionalAdaptor<F, T> adaptor : conditionalAdaptors)
if(adaptor.matches(value)) {
return adaptor.convert(value);
}
return finalAdaptor.convert(value);
}
}
class ConditionConditionalAdaptor<F, T> implements ConditionalAdaptor<F, T>{
private final Condition<F> condition;
private final Adaptor<F, T> adaptor;
public ConditionConditionalAdaptor(final Condition<F> condition, final Adaptor<F, T> adaptor){
this.condition=condition;
this.adaptor=adaptor;
}
@Override
public boolean matches(F value){
return condition.matches(value);
}
@Override
public T convert(F value){
return adaptor.convert(value);
}
}
class AndCondition<V> implements Condition<V>{
final List<Condition<V>> conditions;
public AndCondition(final Condition<V> a, final Condition<V> b){
conditions=new ArrayList<Condition<V>>();
conditions.add(a);
conditions.add(b);
}
public AndCondition(final List<Condition<V>> conditions){
this.conditions=conditions;
}
public AndCondition(final Condition<V>[] conditions){
this.conditions=Arrays.asList(conditions);
}
@Override
public boolean matches(V value){
for(Condition<V> condition : conditions)
if(!condition.matches(value)){
return false;
}
return true;
}
}
class FizzBuzzConditionFactory{
private static final String FIZZ="Fizz", BUZZ="Buzz";
private final Condition<Integer> fizzCondition, buzzCondition;
public FizzBuzzConditionFactory(){
fizzCondition=new IntegerDivisibleBy(3);
buzzCondition=new IntegerDivisibleBy(5);
}
public ConditionalAdaptor<Integer, String> generateFizzBuzzAdaptor(){
return new ConditionConditionalAdaptor<Integer, String>(new AndCondition<Integer>(fizzCondition, buzzCondition), new FixedStringAdaptor<Integer>(FIZZ+BUZZ));
}
public ConditionalAdaptor<Integer, String> generateFizzAdaptor(){
return new ConditionConditionalAdaptor<Integer, String>(fizzCondition, new FixedStringAdaptor<Integer>(FIZZ));
}
public ConditionalAdaptor<Integer, String> generateBuzzAdaptor(){
return new ConditionConditionalAdaptor<Integer, String>(buzzCondition, new FixedStringAdaptor<Integer>(BUZZ));
}
}
class FixedStringAdaptor<T> implements Adaptor<T, String>{
private final String string;
public FixedStringAdaptor(String string){
this.string=string;
}
@Override
public String convert(T value){
return string;
}
}
class IntegerDivisibleBy implements Condition<Integer>{
private final int modulo;
public IntegerDivisibleBy(final int modulo){
this.modulo=modulo;
}
@Override
public boolean matches(Integer value){
return value%modulo==0;
}
}
class ToStringAdaptor<T> implements Adaptor<T, String>{
@Override
public String convert(T value){
return value.toString();
}
}
class IntegerRangeGenerator implements Iterator<Integer>{
private int current;
private final int end;
public IntegerRangeGenerator(final int fromInclusive, final int toInclusive){
this.current=fromInclusive;
this.end=toInclusive;
}
@Override
public boolean hasNext(){
return current<=end;
}
@Override
public Integer next(){
if(!hasNext()) {
throw new NoSuchElementException();
}
return current++;
}
@Override
public void remove(){
throw new UnsupportedOperationException("The integers are a fixed set");
}
}
class Outputter implements Handler<String>{
@Override
public void handle(final String value){
System.out.println(value);
}
}