Using a pinball "kicker" metaphor, Chet and I are going to try a version of the Bowling Game. We'll talk about the metaphor a bit, and then check our intuition that it will work out well.

The Kicker Metaphor

Our basic idea is that we’ll set up ten Frames, and then each roll will be presented to all the Frames in sequence, until one of them consumes it. Preceding Frames may ignore the roll, if their score is complete, or may accept the roll as a bonus ball before “passing it on”. We don’t have a clear picture at this point of who will pass the ball along, what the Frames will be, and so on. We just have a general picture of what will happen.

The mechanism we have in mind is like the “kickers” that are in the holes of a pinball game. A ball hits a hole, and various things can happen. The ball might drop into the hole to say: we call that “being eaten”. The ball might sit there for a while and score some points, then be kicked out to move on. In our vision, this mode will be used for scoring a bonus ball: the kicker will update the bonus value in the Frame, then kick the ball on to go to the next Frame.

Here’s a picture of the metaphor in action on a few different kinds of frames:

image

Now these aren’t intended to be mechanically feasible – darned good thing, too – but to evoke the notion of different kinds of kickers that can be plugged into the Frame to get different effects. Then the ball comes along and drops in, or ticks the kicker and moves on. Maybe for extra credit we’ll try to come up with pictures that might actually work. Probably not, though.

I’m writing this part on Monday night. At this point I don’t have a sense of what the first tests will be, but I have thought a fair amount, with Chet and later without him, and if anything, I know too much about what I think we should do. Since I’ve done the thinking, I’ll share it with you here. Then when we get to doing the work, keep an eye on us to see whether the thinking gets in the way of discovery.

Possible Implementation

I’m thinking that we’ll probably have a Game class, and that we’ll initialize it with ten Frames. Each Frame will be initialized to expect its first roll. When the player rolls a ball, the Game will present it to each Frame in turn, until one eats it.

Each Frame will have a kicker. I’m expecting that there will be various Kicker classes, such as one that accepts the first ball rolled, one that registers a bonus for a spare, one that registers two bonuses for a strike, and so on. I’m not sure whether kickers will change state or whether the Frame will control the kickers, but I’m leaning toward the notion that the kickers will handle most of the work, while the Frames just total up the values.

Now this is a lot more design thinking than I would be inclined to do, but the truth is that I’ve been designing for a long time and I tend to think a lot of design thoughts very quickly, whether I’m just thinking about something that I might implement tomorrow, or whether I’m thinking about design as I go. I’m hoping that I’m practiced enough at letting the code guide the design, so that I won’t fall into any traps or miss any opportunities.

What About YAGNI and BDUF?

Doesn’t the YAGNI principle (You Aren’t Gonna Need It) prohibit this kind of up front design? Well, in a word, no. YAGNI refers to the implementation of features or capabilities or design elements before we need them. YAGNI doesn’t prohibit thinking: thinking is allowed – indeed recommended!

Well, OK but what about Big Design Up Front? Is all this in violation of some XP prohibition of BDUF? Again, no. I’d be concerned if we, or a team, wanted to spend a day or two to design this little thing. A half hour of chat over lunch, some thinking while driving down the road – those shouldn’t be a problem.

I do believe that developers can benefit from working with less design than they might be used to. It’s good experience to use TDD and Refactoring, with very little initial design, in order to learn the power of the techniques. When we work in the Agile fashion, we simply must start with a simple design and then evolve it as the iterations go forward. Practice with TDD and Refactoring will equip us well to keep the design just right.

Starting the Code

We go thru the usual rigmarole to set up a VisualStudio project. One class, named BowlingTestFixture. We’re talking about whether we should proceed as we usually do … test all gutter balls and so on. Those tests don’t describe much mechanism, but we are now talking about having a few classes, maybe even a hierarchy. So we need to wonder when, if ever, we write tests for those things.

The tests we typically write, Chet notes, are almost like acceptance tests – they just say what the BowlingGame does, not how it works. This is true, though we haven’t reflected on it much, if at all. But we can hardly start TDDing the kicker, because we don’t really know yet what we want.

I’m inclined to start with the same kind of tests we usually write, and then see how the design influences what we do and what we test. So here goes, all gutter balls. We’ll skip over the ever-interesting discoveries, like that we need a SetUp and a RollMany, and cut to the implementation of our first test:

using System;
using NUnit.Framework;

namespace BowlingForKickers {
  [TestFixture] public class BowlingTestFixture {
    Game game;

    public BowlingTestFixture() {
    }

    [SetUp] public void SetUp() {
      game = new Game();
    }

    public void RollMany(int count, int pins) {
      for ( int roll = 0; roll < count; roll++ ) {
        game.Roll(pins);
      }
    }

    [Test] public void GutterBalls() {
      RollMany(20,0);
      Assert.AreEqual(0, game.Score());
    }

  }
}

… and code:

using System;

namespace BowlingForKickers {
    public class Game {
        public Game() {
        }

      public void Roll(int pins) {
      }

      public int Score() {
      return 0;
      }
    }
}

The test is green. Ship it.

What’s next? Chet suggests that we need different tests to evoke different code. I’m going to take a contrary position. I want to argue that, given a test that doesn’t work, we’re free to make it work any way we want. But what about “Do the simplest thing that could possibly work”? Is that a rule? Does it force the procedural solution that we know and love? Or can we try some other implementation just because we want to.

Ever the rationalizer, Chet reminds me of the “Express all ideas” rule of simple code. In the procedural code, the Frame shows up as an integer counter. We can certainly express the Frame idea as a class – if we want to. So here’s my plan: we’ll write a test requiring us to add up the Frames, and use that to induce the need for … Frames.

    [Test] public void OpenFrames() {
      RollAll( new int[] {1,2, 3,4, 1,2, 3,4, 1,2, 3,4, 1,2, 3,4, 1,2, 3,4 });
      Assert.AreEqual(50, game.Score());
    }

    public void RollAll(int[] rolls) {
      foreach ( int pins in rolls ) {
        game.Roll(pins);
      }
    }

The above test, and its associated new method, RollAll, is a deviation from my usual practice. The reason is that I have noticed that when I build things with repeated structures like the array of Frames that I have in mind, I sometimes forget to iterate through them, instead processing the same one over and over. After discussion, Chet observes that when we know we tend to make a certain kind of mistake we should develop new habits to avoid it, so we decided to write a somewhat stronger second test. Naturally, it doesn’t actually work, since the Score() method still returns zero, and the Roll method does nothing.

Moving to the Frames Implementation

Our thought-design is an array of Frame objects, to which we’ll present rolls, and that the Frames will eat the balls they like, and process or ignore the ones they don’t. I feel that I have a good idea what to do, and though it is a big bite, I think I can make it.

Chet points out that there will be a fair amount of mechanism down in there, and that at some point we’ll want some tests for it. Or will we? Will the external tests be sufficient for our purposes. We’ve decided to wait and see.

We have in mind a near-trivial Frame object, that eats two balls and then lets the rest pass on. Chet thinks that we should write a test for that and then implement it. It’s hard to argue against that, though I am sure that by the time I’d be done with it, the test we have would have tested it. But there’s an issue: what’s the interface to Frame? We can guess, or we can write the Game code that calls it. Chet thinks we know enough to write the Frame test. I’m not so sure, but maybe all I’m thinking that we’re going to change that interface as the Game and the Frame evolve. But having the tests will be good. We’ll write a Frame test and then build Frame, and then try to use it.

Is this YAGNI? We don’t really need a Frame, do we? Well, we don’t have a line of code that needs Frame, but we do have a design that does, and we’re sure we’ll want to keep it. (We could be wrong.) I say we need Frame now, and therefore it’s not YAGNI.

The first Frame test is:

using System;
using NUnit.Framework;

namespace BowlingForKickers {
  [TestFixture] public class FrameTestFixture {

    public FrameTestFixture() {
    }

    [SetUp] public void SetUp() {
    }

    [Test] public void OpenFrame() {
      Frame frame = new Frame();
      Assert.IsTrue(frame.RollConsumed(3));
      Assert.IsTrue(frame.RollConsumed(4));
      Assert.IsFalse(frame.RollConsumed(2));
      Assert.AreEqual(7, frame.Score());
    }
  }
}

Our thinking is that the Game will send a roll to a Frame using RollConsumed, which will return true if the Frame ate the roll, and false otherwise. We’re not sure of the interface, but it seems good enough to go forward. And the idea is that Frames just pay attention to two rolls, for now, so that it will go false on the third attempt, and will have a score of 7, not 9. Our implementation is:

using System;
using System.Collections;

namespace BowlingForKickers {
    public class Frame {
      ArrayList rolls;
        public Frame() {
      rolls = new ArrayList();
        }

      public bool RollConsumed(int pins) {
      if (rolls.Count == 2) 
        return false;
        rolls.Add(pins);
      return true;
      }

      public int Score() {
        int score = 0;
        foreach ( int pins in rolls ) {
          score += pins;
        }
      return score;
      }
    }
}

The test runs. Now what?

Using the Frame

It may be time to plug the Frame back into the Game. The Game, of course, needs an array of ten Frames, and it needs to present rolls to them until they are eaten. Then we’ll add ‘em up. Again this is a fairly big bite, but we think we’re up to that. We’ll see. If we get in trouble, that will be interesting too.

Well, we did get in trouble. We coded this:

using System;

namespace BowlingForKickers {
    public class Game {
    Frame[] frames;

      public Game() {
      frames = new Frame[10];
        for ( int i = 0; i < 10; i++ ) {
          frames[1] = new Frame();
        }
      }

      public void Roll(int pins) {
      int frameCounter = 0;
      while (frameCounter < 10 && !(frames[frameCounter].RollConsumed(pins)))
        frameCounter++;
      }

      public int Score() {
      int score = 0;
        foreach ( Frame frame in frames ) {
          score += frame.Score();
        }
      return score;
      }
    }
}

The above is the code we wound up with. It’s not quite what it should be; let’s discuss. First of all, what we first wrote didn’t run either of our Game tests, not even the gutter balls one. We should have ditched the code and started over, but first we decided to look around a bit.

We got a first Null Pointer exception due to not initializing the Frame array. So we wrote the loop calling new Frame(), which turns out to have a bug in it that we didn’t find until last.

After initializing the Frames, we still got a Null Pointer exception. I jumped to the conclusion that it was running off the end of the array, and added the frameCounter check logic to Score. I should have paid more attention, since the problem was in Roll, not Score. I did the frameCounter logic wrong once, putting it at the end, then realizing it went at the beginning. Truth is, I think it doesn’t belong there at all. I’ll try removing it in a moment.

The real problem was that my pair stored all the new frames in frames[1]. I spotted it, then he edited that patch of code. I thought he was fixing it but in fact he was just cleaning up the brackets or something So we had to make it say frames[i]. It’s a good thing we have those tests.

     public Game() {
      frames = new Frame[10];
        for ( int i = 0; i < 10; i++ ) {
          frames[i] = new Frame();
        }
      }

Now the tests are running. It’s lunchtime. We’ll chat over lunch, and I’ll comment this afternoon, then we’ll continue tomorrow.

One little tweak: I should be able to remove the frameCounter check. Let’s see … yes, it works without checking frameCounter:

     public void Roll(int pins) {
      int frameCounter = 0;
      while ( !frames[frameCounter].RollConsumed(pins))
        frameCounter++;
      }

I’ll leave the code that way, and bring you up to date on what we talked about at lunch.

Lunch Discussion

We were troubled by the fact that our first try didn’t work when we put in the Frames, and that we actually resorted to the debugger to see what was going on. It did show us right away, however, namely that the frames array was uninitialized except for frames[1].

Then we realized that we were arguably refactoring on a red bar. We could be said to have been implementing, which is OK, but there was at least a refactoring approach that we could have used. We could have turned off the OpenFrames test, then made the GutterBalls run using Frames, perhaps stubbing the Score and Roll method in Frame, then extended Score to work for OpenFrames.

Whichever way you cut it, we wrote a lot of code on a red bar, and we got in trouble. Wiser men might have backed out and tried a smaller test but we thought that the problems would be easy to find. In fact they were, but we agreed at lunch that backing out would have been better. It’s hard to get the gumption to do that, especially when it’s just a few minutes before quitting time. No harm done in this case, but we could have gone down a debugging rat hole. Remember to take smaller steps for a while.

Then we speculated about next steps. We have in mind this little kicker object that will be inside the Frame, that will implement the strategy having to do with which rolls the Frame eats, which ones it records, and which ones it kicks on. We could go ahead and put it in, but right now, there’s no call for it. So our current plan, to be reconsidered tomorrow, is that we’ll implement the “spare”, which should put some logic into Frame, which then we can probably refactor out to create the kicker.

There’s an interesting tension going on here. We have a design in mind, and to a degree, we’re trying to force it in. We keep looking for occasions to put in our cool idea. That’s definitely YAGNI as I understand it. At the same time, I like to think about design, and I feel that it’s useful. Do I really have to discover everything, or pretend to, by starting out with TDD?

Well, certainly not. There is no “must” to all this stuff. But experience today is suggesting that pushing a design element in too soon is tricky, if not downright harmful. Something to think about until tomorrow, at least.

Tune in soon, for another thrilling chapter in the tale of the Bowling Kicker.