FizzBuzz Enterprise Edition
First posted 2007-08-11 00:00:00.000005+00:00
There is a very simple programming challenge called FizzBuzz that Imram reports using to screen out poor programmers. The task is simple. Write a program that counts from 1 to 100 obeying the following rules.
- For multiples of 3 output "Fizz"
- For multiples of 5 output "Buzz"
- For multiples of both 3 and 5 output "FizzBuzz"
- For all other numbers output the number itself
Over on the codinghorror blog there are lots of people proving how clever they are by solving this challenging problem. It's pretty straightforward.
However, many propsed solutions lack proper extensibility and flexibility. Here's a properly enterprise solution.
using System;
using System.Reflection;
using System.Collections.Generic;
namespace TheoSpears
{
public class Program
{
public static int Main(string[] args)
{
for(int i = 1; i < = 100; i++ )
{
NumberDisplay display = NumberDisplayFactory.Instance.CreateNumberDisplay(i);
Console.Write(display);
}
return 0;
}
}
public class NumberDisplayFactory
{
private class NumberDisplayTemplate : IComparable
{
public int Priority;
public Type Type;
public int CompareTo(object obj)
{
if (! (obj is NumberDisplayTemplate) )
{
throw new Exception("Comparing to invalid type");
}
return Priority.CompareTo((obj as NumberDisplayTemplate).Priority);
}
}
public static readonly NumberDisplayFactory Instance = new NumberDisplayFactory();
private List templateList = new List();
private NumberDisplayFactory()
{
Assembly a = this.GetType().Assembly;
foreach(Type t in a.GetTypes())
{
object[] attribs = t.GetCustomAttributes(typeof(NumberHandlerAttribute), false);
if ( attribs.Length > 0 )
{
NumberDisplayTemplate template = new NumberDisplayTemplate();
template.Priority = (attribs[0] as NumberHandlerAttribute).Priority;
template.Type = t;
templateList.Add(template);
}
}
templateList.Sort();
templateList.Reverse();
}
public NumberDisplay CreateNumberDisplay(int number)
{
foreach(NumberDisplayTemplate template in templateList)
{
NumberDisplay candidate =
(NumberDisplay)Activator.CreateInstance(template.Type, new object[] {number});
if (candidate.Matches())
{
return candidate;
}
}
throw new Exception("No matching class found");
}
}
public interface NumberDisplay
{
bool Matches();
}
public class MultipleOfNumber : NumberDisplay
{
private int number;
private int factor;
private string word;
protected MultipleOfNumber(int number, int factor, string word)
{
this.number = number;
this.factor = factor;
this.word = word;
}
public bool Matches()
{
return number % factor == 0;
}
public override string ToString()
{
return word;
}
}
public class NumberHandlerAttribute : Attribute
{
private int priority;
public int Priority
{
get { return priority; }
set { priority = value; }
}
}
[NumberHandler(Priority=50)]
public class MultipleOfFifteen : MultipleOfNumber
{
public MultipleOfFifteen(int number) : base(number, 15, "FizzBuzz") {}
}
[NumberHandler(Priority=10)]
public class MultipleOfFive : MultipleOfNumber
{
public MultipleOfFive(int number) : base(number, 5, "Buzz") {}
}
[NumberHandler(Priority=10)]
public class MultipleOfThree : MultipleOfNumber
{
public MultipleOfThree(int number) : base(number, 3, "Fizz") {}
}
[NumberHandler(Priority=0)]
public class DefaultNumber : NumberDisplay
{
private int number;
public DefaultNumber(int number)
{
this.number = number;
}
public bool Matches()
{
return true;
}
public override string ToString()
{
return number.ToString();
}
}
}