Session 3

Refactoring Programs and Design


810:062
Computer Science II
Object-Oriented Programming


Sailboats and Programs

Let me tell you a story about a simple sailboat company...

The owner rents out boats to tourists, charging them by the amount of time they use the boat. After many happy years of low technology, he decides to automate his office in an effort to maximize his profits. So he seeks the company's first "IT solution": What's the average rental time for his boats?

[... in comes his friend, the UNI grad who took CS II ...]

"Let's model a rental session."

         n  f(i) - s(i)
         Σ  -----------
        i=1      n

The owner, feeling a little concerned about his first foray into high technology, hires some outside help.

[... in comes the big-city "expert" consultant who specializes in "advanced IT solutions" ...]

"But this requires us to pair up the f(i) and s(i) values."

"But we can use a mathematical trick. The formula above is equivalent to this one:"

         n          n
         Σ  f(i) -  Σ  s(i)
        i=1        i=1
       --------------------
                 n

"This model simplifies the task and optimizes the computation."

The client asks us to implement the fancy consultant's suggestion. It works. The client loves us!

Then comes the inevitable second request: What's the longest rental time?

Arrgh.

The client really does love us, so he drops his request and asks for something simpler: Has anyone ever rented a boat for more than an hour?

SIGH.

Final result: we lose a client.

The problem is the design violates the Principle of Continuity:

A change that is small in the business sense should be small in the program.

There should be a continuity between the problem domain and the solution domain.

Object-oriented programming seeks to achieve such continuity by modeling the problem domain more closely. An OO program does not consist of actions on data. It consists of actors that interact to solve a problem.


Follow-Up on Session 2

Do you have any questions about Linux, the reading you did for today, or anything you've done with Java or Junit?

Some answers:

Keep practicing with Linux, vi, and JUnit, and ask any questions!


Back to Our Bowling Game Program

Now, let's return to the BowlingGame. Recall that in Session 1 we wrote a small program to score a game of bowling, based on this specification:

Create an object which, given a legal series of rolls for a complete game of bowling, produces the correct total score.

We broke this high-level requirement down into several smaller requirements:

We then wrote the program in a peculiar way:

  1. Choose one requirement.
  2. Specify how the program will work when solving this requirement.
  3. Write the simplest code to solve it.
  4. If more requirements remain to be done, go to Step 2.

For that second step, we wrote a test in Java and used the JUnit framework to help us run it. Each test we wrote is really a checked example. It's an example of how the BowlingGame class should work, and it is checked every time we run the tests, to be sure that the code does what we think it does!

Take a look at the BowlingGame class we wrote. What are those extra tests for?

Our lecture notes for Session 1 said: "The keys to this approach are small steps, simple code, and cleaning up." But we didn't spend any time cleaning up our program then. What all does that mean? And why do we need to do it?


Cleaning Up

... our program is pretty simple right now, but I'd still like to clean it up a bit. Look at the score() method...

    public int score()
    {
        int score   = 0;
        int current = 0;
        int frame;
        for (int i = 0; i < 10; i++)
        {
            if (roll[current] == 10)
            {
                frame = roll[current] + roll[current+1] + roll[current+2];
                current++;
            }
            else if (roll[current] + roll[current+1] == 10)
            {
                frame = roll[current] + roll[current+1] + roll[current+2];
                current += 2;
            }
            else
            {
                frame = roll[current] + roll[current+1];
                current += 2;
            }
            score += frame;
        }
        return score;
    }

... pretty long ... hides in Java some of the knowledge we had when we wrote it ... what do the two if expressions mean?

[ ... factor out isStrike() and isSpare() methods ... ]

... code should say what it means ... don't lose knowledge when you write your program ... expose knowledge ...

... this is the first time we changed the program without writing a test ... why? ... we didn't add anything new, so there was no new example to write!

... but we did change the code ... how do we know it still works> ... (when) can we trust ourselves?

[ ... run the tests ... if one fails, fix what we broke ... ]

... in this step of programming, each test serves as a change detector, to be sure that the code still does what we think it does, after we change *how* (but not _what_) it does ...

... refactoring to clean up ... makes a *better program*, not a more complete one ... reading versus writing ...

... your programming practice ... constant student lament ...

... after making a reasonable set of changes, make a backup:

    > mkdir version02
    > cp *.java version02
    > ls -l version02

And then move the version02 directory to a safe place.

... let's you back up when a set of changes or additions doesn't work ...

[ ... "version control" ... tool support ... in your IDE ... ]


Wrap Up


Eugene Wallingford ..... wallingf@cs.uni.edu ..... January 11, 2005