Comp 170 Week 8

More on the TechSupport project:

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



Chapter 6: Testing

(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


Chapter 7: World of Zuul

Why do we care about these principles?

Demo of Zuul

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:

  1. Public data fields in Room: VERY bad coupling
  2. poor cohesion in goRoom(), printWelcome()
  3. Code duplication in goRoom(), printWelcome()!!!
Fixes: fixed #1 by adding an accessor getNorth(), and making northRoom private, but that still leaves us a mess when it comes time to adding new directions


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:

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


Steps to Zuul-better:

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 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...



Zuul lab:
Item class: a "trivial" class again (no mutators, or "immutable"; field accessors for each field)



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


Wednesday

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?

Summary of good-code rules:
Review Zuul-bad's issues with code duplication and coupling.

code-duplication: the exit-printing stuff is duplicated

coupling:   
    1. Game class has access to public fields of Room class
    2. The four directions North, East, South, West are built in to the Game--Room interface

Low-level coupling: shared public fields. High-level coupling: shared understanding of valid directions.
Or, to put it another way, data (directions) is encoded into the interface!

responsibility-driven design
The best example here is moving the process of printing out the exits from the Game class to the Room class.
Note that it's still the Game class that calls System.out.println(), but the String it prints is created in the Room class.



Brief summary of types.

Every variable has a type
    primitive types: int, boolean, char, double
    class types: String, TicketMachine, Square, Room, Responder, ....

When you assign one variable to another, the types must match.
When you use something as a parameter in a method, the actual type and the "formal", or "declared", type must match:
    declaration:     enterLot(String description)
    actual:             enterLot("mixed old junk");
   
When you call a method, thing.foo(), then the type of thing must be a class type that has a method foo().

For ArrayList<Thing> AT, the type of an object you add to AT must be Thing:
    ArrayList<Thing> AT = new ArrayList<Thing>();
    Thing th;
    AT.add(th)

All these things are essentially the same: where something of type Thing is expected, only an object of type Thing can be used.


DoME

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.

Dome v3:

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!!