Week 9: Oct 22, 2007 "Test until fear turns to boredom" ===================================================== Overview of problems in zuul-bad 1. Public data fields in Room: VERY bad coupling 2. poor cohesion in goRoom(), printWelcome() 3. Code duplication in goRoom(), printWelcome()!!! Goals: minimize coupling maximize "cohesion" (one job to do) plan for change. Want change to be LOCAL AVOID CODE DUPLICATION Overview of classes in Zuul-good CommandWords Parser Command Room: note public fields! =========================================================================== Zuul-Bad.Room: note public fields! Change one to private & add an accessor: hmm... How well does this work? We're still committed to directions North, South, East, West. Cohesion examples: printWelcome, others ***Examples of code duplication printWelcome, goRoom, etc. currentRoom currentRoom.getItems() currentRoom.getItems().remove(i) Coupling & Cohesion issues Coupling: Zuul-bad had bad (tight) coupling Changes: 1. add new room in direction DOWN. consider this with zuul-bad! Messy! making data private means we can change the implementation. But there's more to coupling than just that: look at agreement of DIRECTIONS ================================================================== Step 1: replace public access direction fields like northExit with an accessor getNorth(); then make northExit private to track where we need to make changes. Big advantage: with the change, we are now free to change the internal representation of exits. But we still cannot easily add a DOWN direction, because the four directions are built in to the interface! ================= Step 2: Problem: directions were part of interface; both Game & Room had to agree on legit directions Note the section of Game.java if (direction.equals("north")) nextRoom = currentRoom.getExit("north"); ... collapses to a single line nextRoom = currentRoom.getExit(direction) If an exit does not exist, nextRoom is null. May also apply if exiting is currently disabled; more later. Also, there is no longer a RESTRICTION on class Room that only the four n/e/s/w directions can be used. ================= Step 3: make printing the exits the responsibility of the Room class. After all, we don't want the Game class guessing at what directions are legal. This is Responsibility-driven design: who prints out the exits? The Room! One option is an ArrayList of pairs. Chosen option: HashMap, Set keys = exits.keySet(); ================ Step 4: more responsibility-driven design Who provides a description of the room? Again, the Room! add getLongDescription() Note this does not PRINT the description; it returns it as a STRING. ================ Step 5: 7.9: more implicit coupling: adding LOOK and look() You have to modify both classes Game and CommandWords .... Worse: adding LOOK to the help string too The first problem is just the nature of having a separate CommandWords class. It's probably worth it. The second can be fixed by adding CommandWord.showAll(). Except we can't call it from within Game.... Solution: have the Parser class mediate... ========================================== Cohesion examples: example of poor method cohesion: Zuul-bad.printWelcome() mixes welcome + exits example of poor class cohesion: having Item desc/weight be fields of Room Cohesion improves readability: classes and methods stick to one topic. Cohesion also improves REUSE: something that does one thing well is more likely to be reusable in another project. Example: Shape/Canvas classes Example: getExit/getLongDesc versus leaveRoom ============================================================================ ============================================================================ Wednesday Test until fear turns to boredom modifying lists issue How big should a class or a method be? Too big: probably bad, but hard to tell when Look to COHESION for the answer: when do you start doing multiple tasks? LockSerpent code: how do we do it??? Bricks debugging (as time permits) =================== Take a look at LockSerpent code. Look at how we have to modify Game class, just to implement one new room. This is NOT good reuse. Complex coupling. ======= Adding steamtunnel (special words: GO, GIVE) and preproom (special word GO, actually special for theater) How theatre works How steamtunnel works, serpent_happy how serpentCommand works Keep in mind: code duplication cohesion responsibility-driven coupling (to the extent applicable) * rigorously avoid code duplication by having serpentCommand *only* handle commands that are different from processCommand; serpentCommand passes everything else back to processCommand to handle. * shoot for good cohesion. Best we can do. changes 1. Add new rooms 2. Move room declarations to fields in Game class, so we can refer to them by name from anywhere 3. Room.setDescription 4. serpent_happy: should be in a room, but we can't do that! 5. Add serpentCommand check to processCommand, ahead of others, so processCommand can still handle anything serpentCommand decides to pass on. ======================================================================= Friday DoME Dome v1 Problem: LOTS of code duplication Dome v2: notice commonality of CD & DVD classes! Use INHERITANCE CD and DVD have something in common. A lot in common. Also, the code that refers to these in Database.java is duplicated. Introduce class Item, containing title, playingTime, gotIt, comment CD, DVD INHERIT from Item, or EXTEND it. superclass, subclass, is-a (really works-like-a) "extends" keyword public/private constructors for subclasses moving super() to later on FIXING PRINT ======================================================