CSSE 220: Object-Oriented Software Development
WordGames

Work on this exercise by yourself, but be quick to ask questions of your instructor, student assistants and classmates as desired.

Goals

Solidify your understanding of how to:

  1. Implement a class:

    • The process for doing so:
      • Writing documented stubs before coding
      • Writing unit tests before coding
        • And especially, becoming better at writing good unit tests
    • Implementing an interface
    • Writing constructors
    • Writing methods
    • Using fields, and particularly deciding what fields are necessary, when
  2. Use objects:

    • Constructing objects with the new operator
    • Invoking methods on objects with the dot notation
    • Reading and applying the API (Application Programming Interface) for a class (namely, the String class)

Grading rubric

Students write 10 classes that implement StringTransformable. Each class is worth 10 points, for a total of 100 points. For each of those 10 classes:

  • Correctness — 10 points per class.
  • JUnit tests — For the Shouter class (and likewise for the other 9 classes that implement StringTransformable), deduct 2 points if its JUnit test class (ShouterTest) does not have a reasonable test called myTest.
    • We supplied JUnit test classes, with tests, for all 10 classes that the students write.
    • Students need only add one test (which should be called myTest) per class.
    • Exception: NameDropper requires more tests, see next bullet.
  • JUnit tests for NameDropper — deduct up to 20 points if NameDropperTest does not have reasonable tests of NameDropper's whose name is NOT the default name (Madonna).
    • These tests can appear anywhere in the NameDropperTest file. In particular, it is fine to place them inside the existing test methods of NameDropperTest.
    • We expect students to add at least 4 reasonable tests, that cover 4 distinct situations, for testing NameDropper.
  • Style — if wrong, deduct up to 4 points for EACH class with style errors.
  • Documentation — deduct up to 4 points for EACH class with documentation errors.
So, a correct project that has no new JUnit tests, horrible style and no documentation would score:
      100 – 20 – 20 – 40 – 40 = -20 points of 100 (yes, that is right — a negative score!)

The grader will not grade the documentation for the JUnit test classes (the existing tests are already documented). Students should use good style in writing tests in the JUnit test classes.

For any points deducted, the grader will put a CONSIDER tag saying how many points were deducted and why.

“Earn back” is available for this assignment.

  • Students: “earn back” is a privilege — don't abuse it. Put forth your “good faith” effort on the project and reserve earn-back for errors that you did not anticipate.

Here is a link to the General Instructions for Grading Programs.

Getting started

  1. Use the class notes associated with this exercise as needed.
  2. In Eclipse, checkout the WordGames project.
  3. You will implement several classes, each of which implements the StringTransformable interface:

    
    /**
     * A StringTransformable object can transform one String into another String.
     * 
     * @author David Mutchler, based on an idea from Lynn Stein in her Rethinking CS
     *         101 project. Created March 12, 2009, updated December 3, 2010.
     */
    public interface StringTransformable {
    
        /**
         * Transforms the given String into another String and returns the
         * transformed String. Does NOT modify the given String (indeed, cannot
         * modify it, since Strings are immutable).
         * 
         * @param stringToTransform
         *            The String to transform
         * @return The transformed String
         */
        public String transform(String stringToTransform);
    }
    
    

    Examine your WordGames project and note that the StringTransformable interface is already added to the project.

  4. Implement each of the classes described below, one by one. For each class that you implement, use this process drawn from Extreme Programming Rules and Practices:

Classes to implement

  1. [Your instructor will do this one with you.] Use the above process to implement:
    Shouter: given blah, produces the result of changing all the characters in blah to upper-case.

    • For example, a Shouter would:

      • transform "Alice in Wonderland" to "ALICE IN WONDERLAND"
      • transform "Tweedledee and Tweedledum" to "TWEEDLEDEE AND TWEEDLEDUM"
  2. [Your instructor will do part of this one with you.] Use the above process to implement:
    Censor: given blah, produces the result of replacing each occurrence of the character (not string) foo in blah with an asterisk, where foo is the character that the particular Censor censors.

    • Thus each Censor has its own character (and just one such character) that it is told about when it is constructed and that it censors. If it is not told about any such character, it will censor the letter 'e' by default.
    • For example, one Censor might censor the letter 'e'. Such a Censor would:

      • transform "Alice in Wonderland" to "Alic* in Wond*rland"
      • transform "Tweedledee and Tweedledum" to "Tw**dl*d** and Tw**dl*dum"
    • But another Censor might censor the letter 'W'. Such a Censor would:

      • transform "Alice in Wonderland" to "Alice in *onderland"
      • transform "Tweedledee and Tweedledum"to "Tweedledee and Tweedledum" (i.e., leave it unchanged — case matters!)

    Reminder: Your code must obey the StringTransformable interface.

    • You may NOT change that interface in any way.
    • So your Censor must have a transform method that takes exactly one argument (the String to transform).
    • If you are tempted to send two arguments to the transform method, or if you think that you need non-private fields, you are off the beaten path and should get help now.
    • It would be wise to get your instructor or an assistant to double-check this method before you continue.

  3. Use the above process to implement:
    Pedant: puts "Obviously " before its given string.

    • For example, a Pedant would transform "Roses are red" to "Obviously Roses are red".
  4. Use the above process to implement:
    NameDropper: puts "foo says " before its given string, where foo is the name that the particular NameDropper drops.

    • Thus each NameDropper has its own name (a String) that it is told about when it is constructed and that it drops whenever it transforms a String. If it is not told about any such name to drop, it will drop the name "Madonna" by default.
    • For example, one NameDropper might be given the name "Confucius". Such a NameDropper would:

      • transform "An apple a day, Keeps the doctor away!" to "Confucius says An apple a day, Keeps the doctor away!"
      • transform "this is silly" to "Confucius says this is silly"
    • But another NameDropper might be given the name "Charlie Chaplin". Such a NameDropper would:

      • transform "very little" to "Charlie Chaplin says very little"
      • transform "Tweedledee and Tweedledum"to "Charlie Chaplin says Tweedledee and Tweedledum".

    For NameDropper, we supplied ONLY tests for NameDroppers that drop the default name (Madonna). YOU must supply tests for NameDroppers that drop a non-default name.

    • We suggest that you add tests to this effect to EACH of the test methods already in NameDropper.
    • Thus, you will write several tests for the NameDropper class, not just one test (as in the other classes).

  5. Use the above process to implement:
    Counter: puts a number in front of each input, that goes 1, 2, 3, ...

    For Example:

    • If a Counter first transforms "Hello", the Counter would produce "1. Hello".
    • If the same Counter next transforms "Goodbye", the Counter would next produce "2. Goodbye".
    • And so forth.
    • Note that each Counter takes what it is given and begins by putting "1. " in front of it.
      • So if you construct one Counter, make it transform some Strings, and then construct another Counter, and then continue making the Counters transform Strings, the Counters are “out of sync” — see test case below.

    A good way to functionally test this class is to:

    1. Construct a single Counter.
    2. Call the Counter's transform method several times.
      • Your Counter should count: 1, 2, 3, ...
    3. Now construct a second Counter, and call both of the Counters' transform methods several times.
      • Your first Counter should continue counting from where it left off: 4, 5, 6, ...
      • Your second Counter should start counting: 1, 2, 3, ...

  6. Use the above process to implement:
    Doubler: repeats the given string two times, with a single space in between.
    • For example, a Doubler would transform "Violets are blue" to "Violets are blue Violets are blue".
  7. Use the above process to implement:
    SlowThinker: produces whatever it was given the previous time.

    For example:

    • If a SlowThinker is asked to transform "Hello", then "Goodbye", then "See you later",
    • that SlowThinker first produces "" (the empty string), then "Hello", then "Goodbye" (and so forth, always one behind its current input).

    As with Counter (and indeed, as with all these StringTransformers), each instance of SlowThinker is independent of other instances of SlowThinker.

    This one is challenging! Don't spend more than 30 minutes on its code (unless you want to!).

  8. Use the above process to implement:
    SometimesShouter: has a positive whole number (I'll call it n). The SometimesShouter capitalizes every nth String that it is given (and leaves other Strings unchanged).

    Each SometimesShouter has its own number (so n should be a field that can be set using a constructor. If it is not told about any such number, it will use 2 (capitalizing every other string it is given) by default.

    For example:

    • One SometimesShouter might have 3   as its value for n. Given the following Strings (in the order listed):

      • How are you today?
      • Not bad. How 'bout you?
      • Not too good, I'm afraid.
      • Why is that?
      • Well, I'm terribly sorry, but I've just run over your cat.
      • Oh no!
      • Well, to make up for it, I'd like to replace your cat.
      • OK, but how are you at chasing mice?
      • -- From the movie Mary Poppins (and many places before that)

      this SometimesShouter would produce (in order):

      • How are you today?
      • Not bad. How 'bout you?
      • NOT TOO GOOD, I'M AFRAID.
      • Why is that?
      • Well, I'm terribly sorry, but I've just run over your cat.
      • OH NO!
      • Well, to make up for it, I'd like to replace your cat.
      • OK, but how are you at chasing mice?
      • -- FROM THE MOVIE MARY POPPINS (AND MANY PLACES BEFORE THAT)

    • Another SometimesShouter might have 4   as its value for n. Given the same Strings as above, it would produce (in order):

      • How are you today?
      • Not bad. How 'bout you?
      • Not too good, I'm afraid.
      • WHY IS THAT?
      • Well, I'm terribly sorry, but I've just run over your cat.
      • Oh no!
      • Well, to make up for it, I'd like to replace your cat.
      • OK, BUT HOW ARE YOU AT CHASING MICE?
      • -- From the movie Mary Poppins (and many places before that)

    Just ask if you have questions about what a SometimesShouter is supposed to do.

  9. Use the above process to implement:
    Repeater: transforms the given String into N   copies of the given String, where N   is the length of the given String.

    For example:

    • If a Repeater is given the String "Hello",
    • then the Repeater produces "HelloHelloHelloHelloHello".

    Another example:

    • If a Repeater is given the String "Goodbye",
    • then the Repeater produces "GoodbyeGoodbyeGoodbyeGoodbyeGoodbyeGoodbyeGoodbye".

  10. Use the above process to implement:
    Evener: transforms the given String into a String consisting of only the characters whose positions in the given string are even.

    For example:

    • If a Evener is given the String "abcdefghij",
    • then the Evener produces "acegi".

    Note that in the above definition, the first character's position is “even”, because we start numbering from 0 (as in the C language) and we consider 0 to be an “even” number.

  11. (Bonus problem, do it only if you are having fun with it!) Use the above process to implement:
    UbbiDubbier: transforms the given String into Ubbi Dubbi.

    • The Zoom site explains the rules for their Ubbi Dubbi language:
      "All you have to do is say UB before every vowel sound."
    • Don't worry if your UbbiDubbier doesn't know how to recognize every vowel sound — that's actually an open research problem!
    • If you wish, you can compare your UbbiDubbier to the Ubbi Dubbi translator at Zoom's site. (Note that theirs is not perfect either—try words with adjacent vowels that belong to different syllables, e.g., idea or Leah).