We have a very procedural implementation of the bowling game, and a very nifty OO version with classes representing states of a state machine. Which implementation is more responsive to new requirements? We'll find out soon!

The Dilemma

Our initial procedural implementation of the Bowling Game is quite clear, we believe, and it’s only fifty or sixty lines long. Our object-oriented version, with sexy Kicker classes representing a state machine, is about 250 lines long. But it’s very cool, very modular, and has very little duplication. Which one is better?

There’s little doubt that the OO version took much longer to build: it’s five times bigger! If that longer time to first shipment is to pay off, we need to get some kind of amazing advantage. The only thing we can think of is that new features should take a lot less time to build. So we’re going to build the same features for both implementations and see what happens.

Desired New Features

We’re going to ask for two new features. One will be to provide an interface that can be used to operate a frame-by-frame display of the game score; the other will be to support the game of Candlepins, a bowling-like game with three rolls per frame instead of two.

Frame Display

A typical bowling scoring machine at the bowling lanes displays the frame status (strike = X, spare = /, open = -), and the numeric score in that frame. The frame displays as much information as it has: none if it hasn’t received any rolls yet, the value of the first roll if it has it, the status if it has it, and the total score of the game up to that frame, if all the rolls needed have been received. Our mission is to implement a feature on the Game that will return a frame status object, at any point during the game, that contains all the information we might wish to display.

Candlepins

The game of Candlepins is played just like bowling, except that you get three rolls per frame. Strikes and spares work just like our version of bowling, scoring two bonus rolls and one bonus roll respectively. Our mission is to provide a CandlepinGame object and all the necessary support for Candlepins, in an expeditious fashion, with as little effort and as little duplication as possible. (We may or may not include the frame status object as part of the Candlepin exercise, depending on whether we think it will tell us anything about the experiment.)

Beginning with Frame Display

Let’s get started … we’re thinking that we’ll create a little FrameDisplay object, a simple record structure that can return the various values we want. The Frame will hold onto it, and will have methods to set values into it. The Kickers will each send messages to the Frame, which will set values into the FrameDisplay. For example, the FirstRollKicker will set the X for strike, if it notices that the frame is a strike, and so on.

We’ll create a new test fixture, FrameDisplayTestFixture, even though those tests will probably mostly test against a single frame. We’ll doubtless write a Game test at some point just for fun. We’ll start with a Strike, because it seems easiest – fewer balls to roll.

The test looks easy enough:

  [TestFixture] public class FrameDisplayTestFixture {
    Frame frame;
    FrameStatus frameStatus;

    public FrameDisplayTestFixture() {
    }

    [SetUp] public void SetUp() {
      frame = new Frame();
    }

    [Test] public void Strike() {
      frame.RollConsumed(10);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
    }
  }

And it directly implies an implementation of the FrameStatus class, and its installation in Frame:

  
public class FrameStatus {

    public object Symbol {
      get { return "X"; }
    }
  }

  public class Frame {
    private int score;
    protected IKicker kicker;
    protected FrameStatus frameStatus; //Frame Status (sarcasm)

    public Frame() {
      score = 0;
      kicker = new FirstRollKicker();
      frameStatus = new FrameStatus();
    }

    public FrameStatus FrameStatus() {
      return frameStatus;
    }    
    ...
  }

Test is green. At this point, a strike frame is not complete, has no score, and has first roll equal to ten. So Chet thinks we should test for those things. Wise man. First, Score:

    [Test] public void Strike() {
      frame.RollConsumed(10);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
      Assert.IsNull(frameStatus.Score);
    }

After trying and failing to figure out how to create a nullable Integer, we decide that the FrameStatus will return only strings. (We could wait for C# 2.0, but that seems tedious.) So now we have FrameStatus:

  public class FrameStatus {
    String score;

    public FrameStatus() {
      score = null;
    }

    public String Symbol {
      get { return "X"; }
    }

    public String Score {
      get { return score; }
    }
  }

Let’s do first roll and complete:

    [Test] public void Strike() {
      frame.RollConsumed(10);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
      Assert.IsNull(frameStatus.Score);
      Assert.AreEqual("10", frameStatus.FirstRoll);
      Assert.IsFalse(frameStatus.Complete);
    }

With “fake it till you make it” implementations:

  public class FrameStatus {
    String score;

    public FrameStatus() {
      score = null;
    }

    public String Symbol {
      get { return "X"; }
    }

    public String Score {
      get { return score; }
    }

    public String FirstRoll {
      get { return "10"; }
    }

    public bool Complete {
      get { return false; }
    }
  }

Tests are green. We’re discussing the fact that because we don’t use the single assert per test approach, we are not seeing a growing number of tests in the NUnit GUI. We could certainly break that test up into several … but then we think that leads us to a whole bunch of fixtures, one for strikes and so on. We’re not motivated to change. We’ll pay attention to see whether this starts to bother us, and your mileage may vary.

We decide to test SecondRoll, with the following test and implementation:

    [Test] public void Strike() {
      frame.RollConsumed(10);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
      Assert.IsNull(frameStatus.Score);
      Assert.AreEqual("10", frameStatus.FirstRoll);
      Assert.IsNull(frameStatus.SecondRoll);
      Assert.IsFalse(frameStatus.Complete);
    }

    public String SecondRoll {
      get { return null; }
    }

We do decide to go to a separate test for the second roll into the Strike, because there’s a separate piece of set up going on. We could just append more rolls after the last assert, but that seems wrong. Plus we’d like to have another bit of green.

    [Test] public void StrikeSecondRoll() {
      frame.RollConsumed(10);
      frame.RollConsumed(5);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
      Assert.IsNull(frameStatus.Score);
      Assert.AreEqual("10", frameStatus.FirstRoll);
      Assert.IsNull(frameStatus.SecondRoll);
      Assert.IsFalse(frameStatus.Complete);
    }

That’s not very interesting, it runs for free. We need to go to the third roll to get a failure:

    [Test] public void StrikeThirdRoll() {
      frame.RollConsumed(10);
      frame.RollConsumed(5);
      frame.RollConsumed(4);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
      Assert.AreEqual("19", frameStatus.Score);
      Assert.AreEqual("10", frameStatus.FirstRoll);
      Assert.IsNull(frameStatus.SecondRoll);
      Assert.IsTrue(frameStatus.Complete);
    }

This should fail and give us something to do. It does. Now what to do. We are considering two options. Ultimately the transition to IgnoreKicker needs to set complete. It could set the score also, or we could give the FrameStatus access to the Frame, and it could ask. So far, FrameStatus is pretty passive – it’s a record object. Should we keep it that way?

Chet has a scheme. We’ll send a message to each Kicker, just as we set it. The message will be SetStatus(this), and the Kicker will call back to us, setting whatever things it thinks we should know. We’ll do that, right now, with complete and score. This turns out to be rather neat. In Frame, we just add this:

  public class Frame {
    private int score;
    protected IKicker kicker;
    protected FrameStatus frameStatus; //Frame Status (sarcasm)

    ...

    public void SetKicker(IKicker aKicker) {
      kicker = aKicker;
      kicker.SetStatus(this);
    }

    ...

    public void SetScore() {
      frameStatus.Score = Score().ToString();
    }
  }

In FrameStatus:

  public class FrameStatus {
    String score;

    public FrameStatus() {
      score = null;
    }

    public String Symbol {
      get { return "X"; }
    }

    public String Score {
      get { return score; }
      set { score = value; }
    }

    public String FirstRoll {
      get { return "10"; }
    }

    public bool Complete {
      get { return score != null; }
    }

    public String SecondRoll {
      get { return null; }
    }
  }

In IKicker:

  public interface IKicker {
    bool RollConsumed(Frame frame, int pins);
    void SetStatus(Frame frame);
  }

And in the top Kicker class:

  public abstract class RollAddingKicker: IKicker {

    ...

    public void SetStatus(Frame frame) {
      return;
    }

    ...
  }

And in IgnoreKicker:

  public class IgnoreKicker: IKicker {

    public bool RollConsumed(Frame frame, int pins) {
      return false;
    }

    public void SetStatus(Frame frame) {
      frame.SetScore();
    }
  }

And all our tests are green. Chet’s idea of calling back to the Kicker during SetKicker is quite elegant. I had been thinking that we would have to send messages during each transition, and now we can just let the various kickers do the job. For example, we can now initialize the Symbol to blank, then fix the resulting redbar by putting a SetStatus() in TwoBonusKicker.

Chet has left now, but I’m going to make this one change and then wrap the session with some comments. The change:

  public class FrameStatus {
    String score;
    String symbol;

    public FrameStatus() {
      score = null;
      symbol = " ";
    }

    public String Symbol {
      get { return symbol; }
    }
    ...
  }

I expect this to break all the checks for “X”. Then, in TwoBonusKicker:

  public class TwoBonusKicker: NonconsumingRollAddingKicker {

    protected override IKicker NextKicker(int ignoredPins) {
      return new OneBonusKicker();
    }

    public void SetStatus(Frame frame) {
      frame.SetStrike();
    }
  }

The implementation is straightforward, but spread around a bit:

  public abstract class RollAddingKicker: IKicker {
    ...

    public virtual void SetStatus(Frame frame) {
      return;
    }
    ...
  }

  public class TwoBonusKicker: NonconsumingRollAddingKicker {

    ...

    public override void SetStatus(Frame frame) {
      frame.SetStrike();
    }
  }

  public class Frame {
    private int score;
    protected IKicker kicker;
    protected FrameStatus frameStatus; //Frame Status (sarcasm)

    ...

    public void SetStrike() {
      frameStatus.SetStrike();
    }
  }

  public class FrameStatus {
    String score;
    String symbol;

    public FrameStatus() {
      score = null;
      symbol = " ";
    }

    public String Symbol {
      get { return symbol; }
    }

    ...

    public void SetStrike() {
      symbol = "X";
    } 

I was confused for a moment, as I always am, by how to set up the override of SetStatus, which seems to require “virtual” in the superclass and “override” in the subclass. C# helps me way too much on this: I guess I’m going to have to read a book about override and new and virtual and abstract and all that. Other than that confusion, everything went in straightaway.

However, upon reflection, I didn’t let the computer help me as much as I like. I actually reasoned my way to all the changes that were needed, and put in all those methods and variables based on my reasoning. The good news is that my reasoning was accurate. The bad news is that I could have let the computer tell me about most of those needed changes. When we do the next test, tomorrow, I’ll try to remember to try it that way and report the details: it’s a much simpler way to work, if your compiles are fast enough.

Assessment So Far

This is going exactly as I expected, with the exception of Chet’s idea of putting a call to the Kicker into SetKicker() and then calling back to the Frame with instructions as to what to do. I had been thinking of making calls to the Frame from each patch of code, like this one, in FirstRollKicker:

    protected override IKicker NextKicker(int pins) {
      if ( IsStrike(pins) ) {
        // inform the Frame here
        return new TwoBonusKicker();
      }
      else
        return new SecondRollKicker(pins);
    }

Chet’s approach is far simpler, as long as you’re happy with a callback, which I am.

While things are going as planned, our time estimates are way off. We (I?) had thought that this whole deal was about a one-session1 story, maybe one and a half, for both implementations. We have a session in now, and we’re not done. We have a bunch of tests to do yet, and they are larger than I had envisioned. The changes we have to make are tiny but kind of spread around. Not spread around in the sense of “changing everything” but in the sense of needing to pass from Kicker to Frame to FrameStatus.

I think we’ve got the rhythm, so tomorrow should go faster, but I bet we don’t start on the procedural version until Thursday. On the other hand, we will have the tests then, so we can focus just on the implementation.

We’ll do another assessment at the end of the Kicker upgrade, and at the end of the whole pass. Then again for the Candlepins game, assuming that still seems interesting. Right now, I think we have a very object-oriented design for the Game, and that our solution for the FrameStatus is very clean … but it isn’t just snapping in trivially. I look forward to seeing what happens with the procedural version. See you next time!

The Code

I’ll put all the code here for the classes we changed, just in case you want to look at it:

  
[TestFixture] public class FrameDisplayTestFixture {
    Frame frame;
    FrameStatus frameStatus;

    public FrameDisplayTestFixture() {
    }

    [SetUp] public void SetUp() {
      frame = new Frame();
    }

    [Test] public void Strike() {
      frame.RollConsumed(10);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
      Assert.IsNull(frameStatus.Score);
      Assert.AreEqual("10", frameStatus.FirstRoll);
      Assert.IsNull(frameStatus.SecondRoll);
      Assert.IsFalse(frameStatus.Complete);
    }

    [Test] public void StrikeSecondRoll() {
      frame.RollConsumed(10);
      frame.RollConsumed(5);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
      Assert.IsNull(frameStatus.Score);
      Assert.AreEqual("10", frameStatus.FirstRoll);
      Assert.IsNull(frameStatus.SecondRoll);
      Assert.IsFalse(frameStatus.Complete);
    }

    [Test] public void StrikeThirdRoll() {
      frame.RollConsumed(10);
      frame.RollConsumed(5);
      frame.RollConsumed(4);
      frameStatus = frame.FrameStatus();
      Assert.AreEqual("X", frameStatus.Symbol);
      Assert.AreEqual("19", frameStatus.Score);
      Assert.AreEqual("10", frameStatus.FirstRoll);
      Assert.IsNull(frameStatus.SecondRoll);
      Assert.IsTrue(frameStatus.Complete);
    }
  }

  public class FrameStatus {
    String score;
    String symbol;

    public FrameStatus() {
      score = null;
      symbol = " ";
    }

    public String Symbol {
      get { return symbol; }
    }

    public String Score {
      get { return score; }
      set { score = value; }
    }

    public String FirstRoll {
      get { return "10"; }
    }

    public bool Complete {
      get { return score != null; }
    }

    public String SecondRoll {
      get { return null; }
    }

    public void SetStrike() {
      symbol = "X";
    }
  }

  public class Frame {
    private int score;
    protected IKicker kicker;
    protected FrameStatus frameStatus; //Frame Status (sarcasm)

    public Frame() {
      score = 0;
      kicker = new FirstRollKicker();
      frameStatus = new FrameStatus();
    }

    public void SetKicker(IKicker aKicker) {
      kicker = aKicker;
      kicker.SetStatus(this);
    }

    public void AddRoll(int pins) {
      score += pins;
    }

    public virtual bool RollConsumed(int pins) {
      return kicker.RollConsumed(this, pins);
    }

    public int Score() {
      return score;
    }

    public FrameStatus FrameStatus() {
      return frameStatus;
    }

    public void SetScore() {
      frameStatus.Score = Score().ToString();
    }

    public void SetStrike() {
      frameStatus.SetStrike();
    }
  }

  public interface IKicker {
    bool RollConsumed(Frame frame, int pins);
    void SetStatus(Frame frame);
  }

  public abstract class RollAddingKicker: IKicker {

    public bool RollConsumed(Frame frame, int pins) {
      AddRoll(frame, pins);
      SetKicker(frame, NextKicker(pins));
      return BallConsumed();
    }

    public virtual void SetStatus(Frame frame) {
      return;
    }

    private void SetKicker(Frame frame, IKicker kicker) {
      frame.SetKicker(kicker);
    }

    protected void AddRoll(Frame frame, int pins) {
      frame.AddRoll(pins);
    }

    protected abstract IKicker NextKicker(int pins);
    protected abstract bool BallConsumed();
  }

  public class IgnoreKicker: IKicker {

    public bool RollConsumed(Frame frame, int pins) {
      return false;
    }

    public void SetStatus(Frame frame) {
      frame.SetScore();
    }
  }  

  public class TwoBonusKicker: NonconsumingRollAddingKicker {

    protected override IKicker NextKicker(int ignoredPins) {
      return new OneBonusKicker();
    }

    public override void SetStatus(Frame frame) {
      frame.SetStrike();
    }
  }

1 A “session” is the amount of work Chet and I can do before lunch, given that we meet about 9 or 9:30 and break for lunch around 11.