Ich habe mich heute mal einfach an die FizzBuzz Kata gesetzt und nach TDD gelöst. FizzBuzz ist meiner Meinung nach die einfachste Kata, die ich hier verwenden möchte um mein Anliegen kund zu tun.
Ich möchte an dieser Stelle nicht alle Schritte erläutern sondern einfach die Frage in den Raum stellen, wann der Code einer Komponente als “clean” gilt.
Um alle Anforderungen der FizzBuzz Kata abzudecken habe ich die folgenden Testmethoden implementiert.
[TestMethod]
public void Fizz_Is_Returned_If_multiple_of_3()
{
// arrange
var fizzBuzz = new FizzBuzz();
var expected = "Fizz";
// act
var valueForThree = fizzBuzz.StringFor(3);
var valueOfSix = fizzBuzz.StringFor(6);
//assert
Assert.AreEqual(expected,valueForThree);
Assert.AreEqual(expected,valueOfSix);
}
[TestMethod]
public void Buzz_Is_Returned_If_multiple_of_5()
{
// arrange
var fizzBuzz = new FizzBuzz();
var expected = "Buzz";
// act
var valueForFive = fizzBuzz.StringFor(5);
var valueForTen = fizzBuzz.StringFor(10);
// assert
Assert.AreEqual(expected,valueForFive);
Assert.AreEqual(expected,valueForTen);
}
[TestMethod]
public void FizzBuzz_Is_Returned_If_multiple_of_3_and_5()
{
// arrange
var fizzBuzz = new FizzBuzz();
var expected = "FizzBuzz";
// act
var valueFor15 = fizzBuzz.StringFor(15);
var valueFor45 = fizzBuzz.StringFor(45);
// assert
Assert.AreEqual(expected, valueFor15);
Assert.AreEqual(expected,valueFor45);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Throws_Exception_if_number_lower_than_1()
{
var fizzBuzz = new FizzBuzz();
fizzBuzz.StringFor(0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Throws_Exception_if_number_is_more_than_100()
{
var fizzBuzz = new FizzBuzz();
fizzBuzz.StringFor(101);
}
Hierbei bin ich wie bei TDD gewohnt Red-Green-Refactor vorgegangen, so dass ich schnell zu einem “Gesamtgrünen” Ergebnis kam, welches wie folgt implementiert ist.
class FizzBuzz
{
private const String Fizz = "Fizz";
private const String Buzz = "Buzz";
public string StringFor(int number)
{
if (number < 1 || number > 100)
throw new ArgumentOutOfRangeException("number", number,
"Number out of Range (1-100)");
if (number % 3 == 0 && number % 5 == 0)
return Fizz + Buzz;
if(number %3 == 0)
return Fizz;
if (number % 5 == 0)
return Buzz;
return number.ToString();
}
}
Eigentlich wäre ich nun an einem Punkt wo ich sagen würde, dass mein Code funktioniert und sauber ist. Doch man kann natürlich nun alle Operationen wie die Prüfung ob die Number innerhalb der Range ist oder die Modulo-Vergleiche aus der eigentlichen Methode StringFor herausziehen.
Bei der Modulo-Operation sehe ich dass ja noch ein, wodurch sich folgendes ergibt:
class FizzBuzz
{
private const String Fizz = "Fizz";
private const String Buzz = "Buzz";
public string StringFor(int number)
{
if (number < 1 || number > 100)
throw new ArgumentOutOfRangeException("number", number,
"Number out of Range (1-100)");
if (IsDivisibleByThree(number) && IsDivisibleByFive(number))
return Fizz + Buzz;
if(IsDivisibleByThree(number))
return Fizz;
if (IsDivisibleByFive(number))
return Buzz;
return number.ToString();
}
private static bool IsDivisibleByThree(int number)
{
return number%3 == 0;
}
private static bool IsDivisibleByFive(int number)
{
return number%5 == 0;
}
}
Ist mein Code jetzt “clean” oder sollte ich eine Methode IsDivisibleBy(int number, int divideBy) implementieren im Stil
private static bool IsDivisibleBy(int number, int divideBy)
{
return number % divideBy == 0;
}
Sollte man eventuell noch die Range-Validierung auslagern in eine ValidateNumber(int number) Methode im Sinne von
private void ValidateNumber(int number)
{
if(number<1)
throw new ArgumentOutOfRangeException("number",number,"Number have to be larger than 0");
if(number > 100)
throw new ArgumentOutOfRangeException("number",number,"Number have to be less than 101");
}
Führt das nicht zu weit?
Wann sollte man mit dem Refactoring aufhören? Wann sind die Operationen atomar genug? Klar kann man alles in eigene Methoden kapseln aber was ist mit YAGNI ??
Für ein paar Ideen oder Anregungen wäre ich sehr dankbar. Ich denke nicht, dass ich der einzige bin, der hier diese schwammige Grenze nicht richtig ziehen kann. Vielmehr sehe ich hier ein menschliches Problem denn wer zieht schon gerne und sicher Grenzen wenn eigentlich keine da sind.
Technorati-Tags:
CCD,
TDD,
Kata,
FizzBuzz