Lab:
Fish With Class
Implementing Classes
 
This set of Lab Exercises is the fifth in a series in which students
build a small program with several fish moving around in an aquarium.
The set includes the following exercises:
	
Each section contains an Introduction to a problem
or task
and an Exercise.
In the exercises that precede this one, students will have created a
list of fish that move randomly back and forth in an aquarium,
being careful not to hit the sides.
Students should understand how to
identify the responsibilities of different classes,
construct class methods,
and use instance variables within methods.
Responsible Fish
Introduction
One of the most important tasks in designing and implementing
object-oriented programs is deciding which classes or objects are
responsible for executing which behavior.
Up until now, all of the
behavior that you have added to the Aquarium Simulation program has been
in the main method of the AquaSimApplication
class.
By now, however, the main method
is starting to get long and tedious to work with.
It makes sense that this method wants to tell all of the fish to
move, but why is it concerned with the details of how a fish
moves, such as when it should reverse direction?  Why isn't the
Fish class responsible for knowing how a fish moves?
It's time to make the program more object-oriented.
In this exercise, you will implement a move method in the
AquaFish class that will encapsulate the 
behavior related to movement, including changing direction and
moving forward.
   Concept: Refactoring
  
  As a program evolves, it is not uncommon to decide that a different
  way of organizing the classes or methods in the program would make it
  easier to read, to understand, or to enhance.  It is not a good idea
  to change the structure of existing code at the same time as adding
  new functionality, because if the program stops working correctly it
  will be difficult to know whether it is the restructured functionality
  or the new functionality that is the cause of the problem.  It is
  better to rearrange or redesign the existing code first, test that
  it still works correctly, and only then add any new functionality.
  The restructuring or redesigning phase, without changing the program's
  behavior, is called refactoring.
  
| Exercise: Make Fish Responsible for Knowing How to Move
            Look through the AquaFishclass.  Just
            before themoveForwardandchangeDirmethods, there is a commented-out
            skeletonmovemethod.  Uncomment themovemethod. Analyze the mainmethod of theAquaSimApplicationclass to determine which lines of code should be moved to themovemethod 
            
            in theAquaFishclass. Move the appropriate lines of code from the mainmethod 
            inAquaSimApplicationto themoveinAquaFish. 
            
            (Stop and Think:
            Everytime themainmethod was
                invokingAquaFishmethods, such asmoveForwardandchangeDir, it was doing so on a specific fish.
                Once that code is moved to themovemethod, what is the specific fish on which those methods
                should be invoked?  How should you invoke the methods? Modify the mainmethod inAquaSimApplicationto simply tell each fish to move, letting the fish worry about how 
            it should move. Now that 
                moveForward,atWall, andchangeDirare only used internally in theAquaFishclass, change the two modifying
                methods (moveForwardandchangeDir) to beprotectedrather thanpublic.  (There are
                commented-out lines that do this already there.  You
                could uncomment them and delete the other lines that were
                being used instead, or you could change thepublickeyword toprotectedin the current lines, using the commented-out lines as a
                template.  (The exact meanings of thepublicandprotectedkeywords
                will be the focus of a future class.) Test your program in the same ways that you have tested it
	  before and make sure that the program behavior is consistent
          with what it was before you refactored it (regression testing).
           | 
 
Concept: Color Representation 
Colors are commonly represented as a mixture of a certain
amount of red, a certain amount of green, and
a certain amount of blue (this color representation is called
RGB, for red, green, and blue).
Each individual color amount is represented by a number between 0 and
255: black is (0, 0, 0) because it has no red, green, or blue; white is
(255, 255, 255) because it is made up of the maximum amount of red,
green, and blue; pure red is represented as (255, 0, 0); while orange,
which is made up of lots of red, quite a bit of green, and no blue, is
represented as (255, 200, 0).  New colors can be constructed in Java by
passing the appropriate amounts of red, green, and blue to the
Color constructor, as in the example below:
        Color myColor = new Color(255, 0, 255);
| Stop and Think
    Look through the AquaFishclass and see how
    the fish's color is represented as part of its state.  See
    where it is set.  How does theAquaSimGUIclass access a fish's color?Why does the AquaFishclass have two constructors?
    What does each one do?  In particular, what does the call tothis(...)in the first constructor do?  
           
 Exercise: Make Fish Responsible for Their Color
            In the one-parameter AquaFishconstructor,
            create randomly colored fish
            rather than white fish.  You will have to do this by
            creating aColorobject with random numbers as
            the parameter to this, without using any local variables.
            (This is because when one constructor calls another
            constructor in the class or its superclass, that call most
            be the first statement in the constructor.)
            In other words, replace theColor.WHITEparameter
            with anew Color(...)expression, where each parameter
            to theColorconstructor is a call to the
            random number generator'snextIntmethod to get
            a random amount of red, green, and blue.
            (Stop and Think: how many
                numbers are there between 0 and 255?  Hint: the answer
                is not 255.  How should you use this information in
                deciding how to get your three random numbers?)Modify the mainmethod to construct fish
            with randomly-generated colors rather than pre-defined colors.Test your modifications.
            
	   | 
Modeling a Simulation
Introduction
In a well-designed object-oriented program, we usually want the
main function to just create some objects and get the ball
rolling.
Most of the program behavior should be the result of the objects
interacting with each other.
In the Aquarium Simulation program, though, the main
function is actually running the simulation.
We have objects that model the fish and the aquarium,
but not one that models the simulation itself.
In this exercise, you will write the code for methods in a
Simulation class.  The constructor will initialize the
Simulation object's instance variables and construct the
fish in the aquarium.
The step method will
execute the commands that should happen each timestep in the simulation
(moving the fish, in our case).
 Once you have implemented the Simulation class, the main 
  method in AquaSimApplication will merely create a number of objects, 
  such as the aquarium, the graphical user interface, and the simulation object, 
  and then ask the simulation object to run the simulation. The main 
  method will also still display the initial configuration of the fish and the 
  modified aquarium after each timestep. 
| Exercise: Introduce the Simulation Class
          
            Create a new Simulationclass in BlueJ.
            When you are done, an object of this class will run the
            aquarium simulation.
            You can delete all of the internals of the class that were
            provided when you created the class (between
            the opening and closing braces), since the template class
            and the class you are creating have very little in common.The new Simulation class will need a constructor and two
            methods: stepandrun.  For now,
            create a public, zero-parameter constructor with an empty body and
            two public, zero-parameter, empty methods that return
            nothing. The main purpose of a constructor is to initialize any
            state (instance variables) instances of the class may have.
            Your class doesn't have a state yet; you can create instance
            variables and initialize them in the constructor as you find
            you need them.  So for now, concentrate on the two methods.
             Start with the stepmethod, since its behavior is
            perhaps the clearest.  Find the code in themainmethod that
            is executed in each step of the simulation.  Move it to your
            new step method.  Notice that your code will not compile
            because there are two objects that it needs that
            it does not have access to: the list of fish and the user
            interface.  For now, pass those two items in as parameters.
            Don't forget to import theArrayListclass at
            the top of your class.
            (Stop and Think: Your code may
                have a third undeclared value representing the number of
                fish.  Why can you make do with just the two
                parameters suggested here, rather than also passing in
                the number of fish as a third parameter? ) Construct a simulation object somewhere in your
            mainmethod
            (Stop and Think: where would be a
                good place in themainmethod to construct
                it?)
            and then call thestepmethod where your code
            for executing
            a single step used to be.
            Test your program. Next implement the runmethod by moving the
            code that runs the simulation from themainmethod.
	    In themainmethod, replace the moved code
	    with a call to therunmethod.
            (Stop and Think: What objects
		need to be passed as parameters to therunmethod?  How should the call to thestepmethod change?) Test your program to verify that the behavior is unchanged.
	
	
	
 Stop and ThinkEvery time themainmethod calls therunmethod, it passes the
        list of fish and the user interface.  Every time therunmethod calls thestepmethod, it
        passes the same objects.  Are those objects changing over time?
        Are the values different every time they are passed to the
        methods, or could the objects be passed to the simulation object
        once when it is constructed and be part of the state of the
        simulation?  In fact, could one or both of them
        just be part of the state of theSimulationobject, constructed in theSimulationconstructor rather than passed to it as a parameter, and not
        exist in themainmethod at all?  Are there other
        objects that you think should be part of theSimulationstate
        rather than in themainmethod?  If the job of themainmethod is just to create a small number of objects
        and then invoke a small number of methods to get the program
        running, which objects should be created there and which should
        be created by the Simulation object?
  Based on your analysis above, move the construction of
          some of your objects from the main method to the Simulation
          class, creating instance variables as necessary.  Don't forget
          to make them private.  Change the
          parameters to yourrunandstepmethods so that you are not passing the object's state around
          as parameters to itself. Test your program to verify that the behavior is unchanged.  | 
Ascending and Descending Fish
Introduction
Our program would be much more interesting if the fish moved up and down
as well as side to side.
In this exercise, you will implement two new methods in the
AquaFish class, ascend and descend,
to support this behavior.
| Exercise: Simulation Up and Down Movement
        To make the simulation more believable, the distance that a fish moves 
          up or down should be related to its height. Fish come in different sizes, 
          so the size of any particular fish is one of the properties of that 
          fish. Its position in the aquarium is another relevant property for 
          this exercise. Read the implementation 
          (code) for the AquaFishclass to determine which methods or instance 
          variables will be useful in implementingascendanddescend. 
          Also look at the class documentation for theNavigationalAideclass to see how to make a fish rise and sink.Determine what parameters (if any) you will need for the new ascendmethod. Then determine what its return type should be. Add an emptyascendmethod (one that consists of a declaration and empty 
          braces) to theAquaFishclass after themoveForwardandchangeDirmethods.Implement the ascendmethod. You may use themoveForwardmethod as a guide if you like, but theascendmethod is 
          simpler. The movement amount should simply be the height of the fish.Implement the descendmethod.Modify your movemethod in theAquaFishclass to allow fish to ascend or descend before moving forward, according 
          to the following formula:
            A fish at the surface has a 2/3 chance of descending and a 1/3 
              chance of staying at the surface.A fish at the bottom has a 1/3 chance of ascending and a 2/3 chance 
              of staying at the bottom.A fish that is neither at the surface nor at the bottom has a 
              1/3 chance of ascending, a 1/3 chance of descending, and a 1/3 chance 
              of staying at the same depth.Test your program. | 
When you are finished modifying
AquaSimApplication, AquaFish, and
Simulation, submit your work.
Copyright Alyce Faulstich Brady, 2001-2002, 2008-2009.