Session 7

Finishing Our Frame Class

Computer Science II
Object-Oriented Programming

Homework 2 Redux

We discussed:

Make simplifying assumptions until you have the problem under control. For example, even if the spec requires your object to handle unsorted arrays, you might assume that your input is sorted. Write a test that assumes as much and write the code to make it pass. Only then do you worry about sorting the array.

If the test you just wrote makes you think, "I have no idea how to make this test pass", then you need to write a simpler test!

Implementing the Frame Class

We ended last session with a Frame class that almost handles spares completely.

Actually, we ended with a broken test: handling a spare in the tenth frame. By stopping with a broken test, we know just where we want to start up, and we know just what to do next: make this test pass:

    public void testSpareInLastFrame()    // tries to score "eleventh frame"
        Frame spareFrame = new Frame();
        for ( int i = 0; i < 18; i++ )
            spareFrame.roll( 0 );

        spareFrame.roll( 5 );
        spareFrame.roll( 5 );
        spareFrame.roll( 5 );
        assertEquals( 15, spareFrame.gameScore() );

I added the comment before we left, because we had some idea of what the problem was before we quit. Why let that knowledge slip away?

Quick Exercise: Describe at least two ways that Frame can pass this test.

The 10th frame must be sure not to send its next frame a gameScore() message. Keep in mind that there may be an "eleventh frame" holding the bonus ball for a 10th frame that is a spare... But how can a Frame know it is the 10th frame?

Either will work. The first option requires an instance variable, which seems a bit more complicated, so unless we can think of a good reason to go that way, I'd prefer the second option. I can't, ao let's go with the second option.

It's easy to add an argument to gameScore() and have the Frame pass an incremented value to its next frame.

    public int gameScore( int frameNumber )
        if (( nextFrame == null ) || ( frameNumber == 10 ))
            return score();
        return score() + nextFrame.gameScore( frameNumber + 1 );

But this requires users of the Frame to pass a 1 when it sends the gameScore() to the first frame. But should it have to?

Discuss why this is a bad idea... Pushing implementation detail out of the class into the space where objects communicate. Make analogy to working with people...

What is the alternative? Maintain gameScore() as an interface method:

    public int gameScore()
        return gameScore( 1 );

    private int gameScore( int frameNumber )
        if (( nextFrame == null ) || ( frameNumber == 10 ))
            return score();
        return score() + nextFrame.gameScore( frameNumber + 1 );

Run the tests. They pass! Run the OpenFrameTests, too. They pass. Success.

Discuss why this is a better idea... Hiding implementation detail from the objects with which Frames communicate. Step through the "recursive" nature of the algorithm. Distinguish from ordinary recursion in which a method within an object calls itself. Here the recursion is via simpler objects... The idea of Object Recursion.

Note: We cannot avoid this issue by opting for the first design option above, creating Frame objects with their frame numbers. That approach encounters the same problem, only in the form of its constructors!

The Final Frontier: Strikes

We are almost there. Strikes are not a lot different than spares, so the code we wrote to handle spares has prepared us well for strikes. The new issues are:

But, as always, we'll start simple and let our tests lead our code to these issues in good time.

What's the simplest test case to consider? We can use our design thinking from having implemented spares:

Implementing Strikes

Walk through the test cases one at a time.

Voilá! Our Frame class seems to do the trick. Be sure to run OpenFrameTest and SpareFrameTest, too. We don't want our new functionality to break prior tests...

Now all we have left is to fold this back into our BowlingGame -- and implement the new requirement that drove us here.

Wrap Up

Eugene Wallingford ..... ..... February 1, 2005