Monday, March 22, 2010

bowling kata ocp way (extended comment)

Some more notes about my "ocp bowling kata".

An execution of a generic bowling is a serie of frames, each composed of some rolls.

Se sequence {5,5} is allowed in a single frame. The sequence {10,1} is not, because if the first roll in a frame is 10 then the frame is over.

To model those validation is used the Delegates technique, introducing constraints witch return true or false on a frame to check if the frame is valid according to some specific bowling instance.

Here there are the constraints needed for the plain ordinary frames from index 0 to 9:

Constraint sumOfAllRollMustbeLessThanTen =
(x => x.Rolls.Sum() <= 10);
Constraint ifFirstRollIsTenThanTheFrameIsOver =
(x => (!(x.Rolls[0] == 10) || x.Rolls.Count == 1));
Constraint ifFirstRollIsLessThanTenThenThereIsAnotherRollInTheFrame =
(x => (!(x.Rolls[0] < 10) || x.Rolls.Count == 2));

Those constraints are composed (by &&) and injected using the call: SetConstraintForFrame().

There is the possibility that a frame with different index has different constraints.
The 10th frame in the terrestrial bowling in fact has different constraints:

Constraint sumRollsNoHigherThanThirty =
x => x.Rolls.Sum() <= 30;
Constraint ifFirstRollIsTenThanThereIsAtLeastAnotherRoll = x =>
!(x.Rolls[0]==10)||x.Rolls.Count > 1;

Constraint ifSecondRollIsTenThenThereIsAnotherRoll =
x => (!(x.Rolls.Count > 1 && x.Rolls[1] == 10) || x.Rolls.Count == 3);

(see the factory)

Note that the following logical rule has been used:
if A then B is the same as !A || B

There are also rules to calculate the score and the bonus.

The Spare rule says that there is a bonus given by the next roll, and the Strike rule returns as a bonus the next two rolls.

Score is divided by the plain score and the bonus, so also the plain score rule (generally given by the rolls) can be injected.

I still don't know if unit test should allocate frames or single rolls.

At the moment there are both in the tests .

For instance to compute zero score using frame is as follows:

public void TestAllZeroes()
Frame frame = new Frame(0,0);
for (int i = 0; i < 10; i++)
Assert.AreEqual(0, terrestrialGame.Score());

and the following is the equivalent that uses rolls:

public void TestAllZeroesByRolling()
for (int i=0;i<20;i++)

To test the constraints I need ti add frames in the test, but i can also say that the user should be exposed just the Roll method, so the method that get frames could become private.

That means eliminate the tests based on frames (if we don't want to test private methods).

The "bowling marziano" is based on just three frames of no more than tre rolls, and for any strike the bonus is the result of the last frame.

No comments: