|
home—info—archive—exams—lectures—labs—hws
Recipe—Laws—lies—syntax—java.lang docs—java.util docs
99 bottles of rootbeer on the wall,
99 bottles of rootbeer;
take one down,
pass it around,
98 bottles of rootbeer on the wall.
98 bottles of rootbeer on the wall,
98 bottles of rootbeer;
take one down,
pass it around,
97 bottles of rootbeer on the wall.
[etc., ad nauseum]
— anonymous1
Write a function oneVerse, which takes in a number-of-bottles, and returns exactly one verse (exactly 5 lines) of that eternal classic, “99 bottles of rootbeer on the wall”.
If you want to see your result formatted in a screen-specific way
on the console,
you can go to BlueJ's code pad and call the static method
System.out.print2
yourself,
passing it the result of calling your function.
For example, call
BlueJ Terminal Window tip: It's helpful to select the menu options Options > Unlimited buffering as well as Options > Clear screen at method call. These options are only in the menu when BlueJ's terminal window has focus3
You might wonder why we don't have our function not bother returning a String, but just print directly to the console window. That's fine, if all you ever want to do is turn console-window pixels on and off. However, by returning a String instead, our code is much more flexible: People can call our method and then use the resulting string for whatever it wants (sending it to a speech-synthesizer, writing it to System.out, writing it to a file or a web-page, or even email a single verse of the song to your parents each minute for 100 minutes). If our method hadn't returned a String, we'd be pre-emptively crippling future uses of our method.
We realize that our code needs to do something over-and-over, so we'll want to use a loop. Which type -- for-each, or while? Well, we don't already have a list-of-somethings to work with, so a for-each loop won't do. That leaves trying a while loop…
BlueJ tip:If BlueJ's progress-bar is churning away and you think your program is an infinite loop, you can right-click on the progress bar to stop it.
Write a function entireSong which takes no inputs, and returns the entire lyrics to the song. Don't repeat any code; instead just call a method which already computes sub-answer this method wants.
Use a named-constant to represent where the song starts. For testing, change the named-constant from 99 to (say) 4. Just call your method and give the result to System.out.println.
ITEC120 does not condone underage drinking, nor in peer pressure to drink rootbeer. It would be nice to provide users with a version of our function which allows the caller to specify what sort of drink they'd like 99 bottles of. For example,
oneVerse( 17, "ginger ale" ) = "17 bottles of ginger ale on the wall, 17 bottles of ginger ale; take one down, pass it around, 16 bottles of ginger ale on the wall. " |
Your task
Make overloaded5
versions
of each of your three previous methods so that it they
can each take an additional argument: the beverage to drink.
(You'll temporarily have repeated code, as each method
will have a lot of code in common with each rootbeer-specific method.
Don't worry; we'll fix that in the following steps.)
Similarly, get rid of repeated code by gutting the old versesFromTo(int,int) and entireSong(), replacing their body with a single call to their more generalized cousins.
Optional, but highly recommended:
If your boss suggests that the default drink
should be something changed from “rootbeer” to
something more hip, like “boba tea”,
How many places does your code need to be changed?
Hopefully,
one word from your boss requires only
one change in your code!
(Yes, the caller can already specify a particular drink; this problem is talking about what default is used whenever the caller doesn't specify.)
The approach taken above has not been object-oriented: all the methods are static. How would we design things differently, if we decided that a user might make mutliple BottleSingers? What information (fields) might each singer have?
Each singer might have some notion of where they are, in the overall song. Write a method String nextVerse(). (This allows the user to have a BottleSinger sing one verse, wait a while, and then just ask the next verse, without the user needing to keep track themselves of what verse to sing.)
There are two obvious ways you might have a field to represent info: either what verse the server has sung most recently, or what verse they will next sing when asked. Either way is okay, but name your field appropriately. Note that while we don't want a public getter for this field in general, for writing test cases it might be useful to have a getter which returns this. Make that getter neither public nor private6
Of course, make some test cases before writing nextVerse. You might find it more convenient to edit the unit-test itself, rather than use BlueJ's “record” feature.
On Friday, we will check off through #7. We'll check that (a) you pass all the provided test cases, and (b) no repeated code: four methods should be a single line, one should be three-five lines (declare/init accumulator; loop control; loop body; return)
1Anonymous, and extremely annoying ↩
2We don't need to use println, as our method already icludes newlines characters in the returned String. ↩
3You can give the terminal window focus by either (a) calling System.out.println, or (b) selecting View > Terminal. ↩
4If you really wanted no blank line after the last verse, it's a bit annoying. Approaches include: (a) Have your loop handle the first verse (if it exists) specially; then have a loop which handles all the rest (prepending a newline each time). (b) including an 'if' statement inside the loop, to check for the last verse specially; (c) adding a newline after every single verse, and then remove the last character (retroactively un-doing part of the last iteration — Yech!). None of these are aesthetically pleasing, though both (b) and (a) are acceptable. If you do one of these, you'll also want to edit the unit-test file, so that it doesn't expect the newline at the end of the last verse. ↩
5 Remember that overloading is a shallow concept: If your language didn't have overloading (like Java does), you wouldn't really mind; you'd just call the methods oneVerse_v1(int) and oneVerse_v2(int,String). When other people call your method, they are fully aware of whether they are passing in one argument or two, so they are fully able to decide whether to call _v1 or _v2. However, overloading is convenient: it means people need to remember fewer method names, especially for closely-related functions. ↩
6 This makes the getter “package protected”, which means that everybody inside the package can access it; the unit-test is inside the same package. ↩
home—info—archive—exams—lectures—labs—hws
Recipe—Laws—lies—syntax—java.lang docs—java.util docs
©2008, Ian Barland, Radford University Last modified 2008.Apr.10 (Thu) |
Please mail any suggestions (incl. typos, broken links) to ibarlandradford.edu |