What would it take to have a set of responses for each keyword,
and to pick different ones at random?
Perhaps a HashMap<String, ArrayList<String> >
From a string (eg "bug") we get a list of responses.
What would it take to match two-word pairs, in order?
We couldn't use the built-in HashMaps; we'd have to write our own "matcher" class.
Quick demo of balls project, chapter 5
Look at BouncingBall.move()
private static final int gravity = 3;
compare to ballDegradation
(We're going to defer this until after Zuul)
Example done in class:Debugger demo with pldtech, as modified.
There are two errors. One of them causes termination; the other causes the questionMap to be skipped.
Use of the debugger to find both (actually, the first one isn't helped that much by the debugger)
basic idea: use breakpoints in the program
Why do we care about these principles?
Bad version of Zuul, then good version. Discuss differences.
Goals: Loose COUPLING
COHESION: a class should represent one thing; each method should do one thing
non-cohesive: Iterator.next() returns current item & moves on. but this is probably ok
Zuul-bad problems:
Overview of problems in zuul-bad:
Goals:
Overview of classes in Zuul-good
CommandWords Parser Command
Room: note public fields!
Zuul-Bad.Room: note public fields!
Demo: change one to private, and 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
Possible changes:
Suppose we want to add new room in direction DOWN. Consider doing 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: Get directions out of the room interface
In Zuul-bad, directions were part of interface; both Game & Room had to agree on legitimate 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 owns the exits? The Room!
Who prints out the exits? The Room!
One option is an ArrayList of
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
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?
Dome v1
Problem: LOTS of code duplication
CD and DVD use much of the same (duplicated!) code
The main Database class has to use the same code for CDs and then for DVDs
addCD v addDVD
dual loops in list()
Core problem: CD and DVD are similar, but not the same.
Dome v2
Notice commonality of CD & DVD classes! This version uses inheritance. What good does this do?
CD and DVD now have their shared parts moved to Item, containing title, playingTime, and gotIt.
CD, DVD inherit from Item, or extend it.
superclass, subclass, is-a (really works-like-a)
extends keyword
Note also that in Database, we have one method addItem(Item theItem), and one method list(), with one loop.
Also note that inheritance lets us address "responsibility" very precisely, for responsibility-driven design.
Initialization of subclasses (8.4.2)
public/private
constructors for subclasses
moving super() to later on
Adding VideoGame
How Database changed
How did we get away without having separate addCD and addDVD, and separate print loops?
Because an ArrayList<Item> can have CD and DVD objects added to it!
Or, to put it another way, since a CD "is an" Item, we can put a CD object everywhere an Item is expected:
assignment
parameter
ArrayList<Item>
This is a small but very important change in the type rules for
java: a subclass type is considered to be acceptable when the parent
type is expected. This is the substitution principle. Note that it only goes one way.
8.7.2: Vehicle, Car, Bicycle
A Car is a Vehicle
A Bicycle is a Vehicle
A Vehicle is not a Car (not necessarily, anyway)
A Vehicle is not a Bicycle
A Car is not a Bicycle
A Vehicle has some basic attributes: numPassengers, maxSpeed, numWheels.
Car adds some things, but retains the Vehicle attributes. Any question you can ask about a Vehicle makes sense for a Car.
Ditto for Bicycle.
But a Car has an Engine. Vehicles do not neccessarily.
Another term for this is polymorphism: base-class variables are polymorphic.
Fixing print()
Note that in v2 it does not work right. There appears to be no good
solution! How can we reconcile inheritance, avoidance of
code-duplication, and responsibility-driven design?
Problem 1: print() in Item can't be CD/DVD-specific!
So we move print() to CD and DVD. We then get
Problem 2: the class doesn't compile.
So we put it both places.
Problem 3: prints only the CD/DVD-specific information!
Java most-specific-method rule!!