The Beginning of Extreme Programming

Kent Beck's Smalltalk implementation of '99 Bottles of Beer On The Wall' was the spark that launched Extreme Programming.

As a member of the Chrysler C3 team, I was challenged to learn how to build systems using Object Oriented techniques. One of the tools I and others on the team used was the comp.lang.smalltalk discussion group. Late in 1995, someone posted a message to most of the comp.lang groups asking the a program be posted in the group's language showing how the lyrics to the song '99 Bottles of Beer on the Wall' should be generated. There were several postings in the Smalltalk group, most of which were not too inspiring. I then saw the code listed below. A little while later, we decided we needed help performance tuning our application, and I suggested we talk to this Beck guy.

It turns out that our code was too much like that that did not impress me and with Kent's (,and Ron Jeffries's, Martin Fowler's, Jim Huangs's, and Ken Auer's) we built a system that looked much more like this one.

Subject: Re: 99 bottles of beer, auf Smalltalk

Date: 1996/01/26 Author: Kent Beck

There has been a discussion recently on comp.lang.smalltalk about how best to represent the song "99 Bottles of Beer on the Wall" in Smalltalk. Apparently, there has been some use of this song as a way of benchmarking various programming languages.

I found the Smalltalk implementations appalling in their lack of use of the facilities that make Smalltalk so powerful. In particular, the previously published solutions all made heavy use of iteration and conditional logic.

In my quest for intellectual purity, the limits of absurdity, and a decent distraction from this nasty cold, I have undertaken to write the one true definitive Smalltalk version. I hereby place the following code, written in literate program form, into the public domain, to stand as a monument for the ages, or until my hard disk crashes again, whichever comes first.

Specification

We need a program which takes as input, N, a number of bottles of beer and produces as output a string containing the N+l verses of the bottles of beer song. For N=3, the expected output is:

'3 bottles of beer on the wall 3 bottles of beer Take one down and pass it around 2 bottles of beer on the wall

2 bottles of beer on the wall 2 bottles of beer Take one down and pass it around 1 bottle of beer on the wall

1 bottle of beer on the wall 1 bottle of beer Take it down and pass it around No bottles of beer on the wall

No bottles of beer on the wall No bottles of beer There are no more to pass around No bottles of beer on the wall '

Note the subtle changes, verse to verse, that will necessitate the application of powerful object-oriented techniques. The third line changes, as does the plurality of the number of bottles.

Verse

The fundamental question in any object oriented development is "what is my first object?" Until this question is answered aright, nothing else the developer does will be of much use. Fortunately, we have in "song" a natural unit of computation, the Verse. Introducing varying kinds of Verses will allow us to capture with polymorphic messages logic that must be expressed as explicit conditionals in procedural languages. Such future flexibility is of uncertain use with a problem like BOB (Bottles of Beer song), but that's the thing about future requirements, you don't know what they're going to be.

 Object
   subclass: #Verse
   instanceVariableNames: 'stream'
   classVariableNames: ''
   poolDictionaries: ''

Each Verse will sing itself onto a Stream by singing its verse, then asking the next verse to sing itself.

 Verse>>sing
   self singVerse.
   stream cr.
   self nextVerse sing

The recursion ends when you get to a Verse which represents a negative number of bottles of beer, the NegativeVerse. Many developers would have subclassed Verse to define NegativeVerse, but since it need only implement a single message, sing, and since there is no code sharing involved, I chose to subclass directly from object. This leaves me the option to factor Verse inheritance some other way, should that prove important in the future.

 Object
   subclass: #NegativeVerse
   instanceVariableNames: ''
   classVariableNames: ''
   poolDictionaries: ''
 
 NegativeVerse >>sing
   "Do nothing"

We will use other variations on Verse to capture the special cases in the song. PrecedingVerse will sing the Nth to second verse. PenultimateVerse will sing the verse in which there is one lonely bottle left, and UltimateVerse will sing that sad sad verse in which all the bottle are gone and their contents drained.

The Verse instance creation method is a Factory Method, returning a Verse of the correct subclass depending on the bottle count.

 Verse class>>Count: aninteger stream: aStream
   aninteger < 0 ifTrue: ^NegativeVerse new
   aninteger = 0 ifTrue: ^UltimateVerse stream: aStream.
   aninteger = 1 ifTrue: ^PenultimateVerse stream: aStream.
   ^(PrecedingVerse stream: aStream) setCount: aninteger

Singing a Verse

The singing of a Verse is represented by a method, each line of which causes the singing of one line of the song. In the future, it may be wise to further objectify the solution by creating a family of Line objects which sing individual lines . At this time, however, the solution does not seem to require this flexibility.

 Verse> >singVerse
   self
     bottlesOfBeerOnTheWall;
     bottlesOfBeer;
     takeOneDown;
     nextVerseBottlesOfBeerOnTheWall

The First Line

The first line is sung by singing the number of bottles, followed by "of bottles of beer on the wall".

 Verse>>bottlesOfBeerOnTheWall
   self countBottles.
   stream
   nextPutAll: ' of beer on the wall';
   cr

The count of bottles of beer is sung by singing the number of bottles, followed by a space and the word "bottle", appropriately pluralized:

 Verse>>countBottles
   stream
     nextPutAll: self bottleCountString;
     space;
     nextPutAll: self bottlesString

The default implementation of bottleCountString prints the number of bottles in the receiver:

 Verse>>bottleCountString
   ^self count printString

On the last verse, however, the word "No" is printed, rather than a number. This is represented by overriding bottleCountString in UltimateVerse:

 UltimateVerse>>bottleCountString
   ^'No'

The pluralization of the word "bottle" is handled by implementing the default plural version in Verse, and overriding in PenultimateVerse:

 Verse>>bottlesString
   ^'bottles'
 
 PenultimateVerse>>bottlesString
   ^'bottle'

Counting and the Various Verses The count of bottles is explicitly represented by an instance variable in PrecedingVerse:

 Verse
 subclass: #PrecedingVerse
 instanceVariableNames: 'count'
 classVariableNames:
 poolDictionaries: ''

 PrecedingVerse>>setCount: aninteger
   count := aninteger
   PrecedingVerse>>count
   ^count

The other verses, subclasses of Verse with no additional instance variables, represent their counts implicitly in the accessing method for "count":

 PenultimateVerse>>count
   ^1
 UltimateVerse>>count
   ^0

Succeeding Lines

The second line is computed in much the same way as the first.

 Verse>>bottlesOfBeer
   self countBottles.
   stream
    nextPutAll: ' of beer';
    cr

The third line shows variation in both the last and second to last verses. All the other verses sing it with:

 PrecedingVerse>>takeOneDown
   stream
     nextPutAll: 'Take one down and pass it around';
     cr
   
 PenultimateVerse>>takeOneDown
   stream
     nextPutAll: 'Take it down and pass it around';
     cr
   
 UltimateVerse>>takeOneDown
   stream
     nextPutAll: 'There are no more to pass
     around';
     cr

The final line is a bit strange. The last line of one verse is the same as the first line of the next, so the obvious implementation is to send "self nextVerse bottlesOfBeerOnTheWall". However, the result of sending nextVerse to an UltimateVerse is a NegativeVerse, which doesn't know how to bottlesOfBeerOnTheWall. We would either have to duplicate the code from UltimateVerse in NegativeVerse or somehow send a message back to the UltimateVerse. Instead, we explicitly encode the fact that the final two verses have the same last line by hiding it behind a concatenated message, nextVerseBottlesOfBeerOnTheWall. The default implementation is simple:

 Verse>>nextVerseBottlesOfBeerOnTheWall
   self nextVerse bottlesOfBeerOnTheWall
 
 UltimateVerse overrides this to send itself the
 message bottlesOfBeerOnTheWall:
 UltimateVerse>>nextVerseBottlesOfBeerOnTheWall
   self bottlesOfBeerOnTheWall

Utilities

Instances of the subclasses of Verse are created by passing along the Stream to be sung on. This is not public protocol, however, and is intended to be used only by the Verse class>>count:on: method.

 Verse class>>Stream: aStream
   ^self new setStream: aStream
 
 Verse>>SetStream: aStream
   stream:= aStream

The next verse is computed by decrementing the count by one and sending it to the FactoryMethod in Verse:

 Verse>>nextVerse
   ^verse
     count: self count - 1
     stream: stream

Finally, Verse provides a convenient Facade to sing any number of verses:

 Verse class>>sing: aninteger
   |writer|
   writer := WriteStream on: String new.
   (self
     count: aninteger
     stream: writer) sing.
   ^writer contents

Performance

This program's use of a Stream for concatenation makes it generally efficient. Here is a performance profile run with Profile/V (you were just waiting for the commercial plug, weren't you) and gathered on the recursive call to Verse>>sing.

100% (177) Verse>>sing ...

88% (155) Verse>>SingVerse ...
:31% (54) Verse>>nextVerseBottlesOfBeerOnTheWall ...
: :25% (44) Verse>>bottlesOfBeerOnTheWall ...
: : :20% (35) Verse>>CountBottles ...
: : : :14% (25) Verse>>bottleCountString ...
: :5% (8) Verse>>nextVerse
:26% (46) Verse>>bottlesOfBeerOnTheWall ...
: :20% (35) Verse>>countBottles ...
: : :15% (26) Verse>>bottleCountString ...
:22% (39) Verse>>bottlesOfBeer .. .
: :18% (31) Verse>>countBottles .. .
: : :10% (18) Verse>>bottleCountString ...
:8% (15) Verse>>takeOneDown .. .
10% (17) Verse>>nextVerse .. .

NextVerse is redundantly computed twice, so its total of 15% could be cut in half. BottleCountString is computed three times, so its total could be cut from 39% to 13%. This could be further reduced by printing the bottle count directly on the stream, without creating an intermediate String.

Final Comments

With a bit of work, this program could be expanded to encompass a framework for singing repetitive songs of all kinds. At present, however, it stands as a monument to that sense of the ridiculous that is all that gets us through some times. Long may it wave.

Kent Beck

Add ping

Trackback URL : https://leftsideagile.com/index.php?trackback/5

Page top