Lab 12: Adaptor and Decorator

The goal of this lab is to practice using the Adaptor and Decorator patterns.

1. Initial setup

Copy the current version of the simulation code into your directory. This "current version" uses the strategy pattern for hunting, but the only supported strategy is one called RandomStrategy, in which the agent reacts to a random neighbor.

cp /cslab/class/cs245/lab12/* .

There is one other change: There is now a class TorusPPGrid which is just like PredPreyGrid (in fact, it's a subclass) except that it wraps around---the left side of the grid continues into the right side, and the north continues into the south. Currently the program still uses the old PredPreyGrid, but you may change PredPrey to use the new one if you'd like.

2. Part I: Adaptor

A. Introduction

Evil Professor NenurdNav has prepared his own version of a predator-prey simulation for Lab 21 of his course CS 542. It is similar to ours conceptually, but all the code is different. It includes a class Snake, which has some interesting behavior:

You like this idea and want to plug this Snake class into our version of the simulation. However, it uses all different interfaces. You must use the adaptor pattern in order to make the Snake class work in our simulation.

B. Setup

Copy all the files from a directory where I've put the alien code. This includes Snake.java and three interfaces that Snake depends on.

cp /cslab/class/cs542/lab21/* .

C. Writing Adaptors

Your task here, after you have looked over the files and gotten a basic feel for how they work, has four parts:

  1. The main part is to write an Adapter class for Snake. This class will have to be a subtype of Agent, but it is up to you whether it should be a subclass of Animal.
  2. Snake, however, depends on Island instead of AgentGrid and Organism instead of Agent You will need to write an Adaptor for Agent to make it look like an Organism. This should be fairly easy.
  3. Wrapping AgentGrid, however, is more complicated. For one thing, Island has methods that take not just a pair of coordinates, but a pair of coordinates plus a distance and direction, indicating the position which is the given distance away from the given coordinates, in the given direction.
    The other complication is that putOrganism() receives an Organism as a parameter, but it must put an Agent into the grid. The Organism might be a Snake, in which case it must be wrapped; it might be an adapted Agent, in which case it must be unwrapped; or it might be null, in which case nothing needs to happen, except that the compiler must be satisfied that you're using the right types.
  4. Finally, you will need to adapt PreyArbitor for the PredatorDeterminer interface. This is trickier because PreyArbitor is used only for static purposes, whereas a Snake expects a PredatorDeterminer object.

With all this wrapping and unwrapping of Snakes, you should make sure that you do not create multiple SnakeAdaptor instances for the same Snake instance. I recommend having a private static HashMap in SnakeAdaptor which associates Snakes and their adaptors. Have a static method in SnakeAdaptor which takes a Snake and stands in for a constructor: if the Snake is not in the HashMap, then make a new adaptor for it; otherwise, return the old adaptor you already made.

D. Testing

To test your changes, you will need to make some modifications to PredPrey.java and predprey.dat.

3. Part II: Decorator

Next, add a new feature to the simulation: a disease which infects animals. A diseased animal will act the same way as it would otherwise except that

Implement this by writing a class DiseasedAnimal which is a decorator for the Animal abstract class. You will need to modify PredPrey.java to test your changes. You will also (unfortunately) need to change PreyArbitor to deal with diseased animals.

The difficult part is that the animal internal to the DiseasedAnimal object doesn't know it's diseased, so when it moves it will simply add its (undiseased) self back into the grid. To deal with this, you will need also to decorate AgentGrid: Write a class which wraps the old grid, implements AgentGrid and also knows both the diseased version of the animal and the internal animal. When the internal animal tries to move itself in the grid, the wrapper should catch that and move the diseased animal instead.

4. Turn in

Turn in a hardcopy of the classes you wrote.


Thomas VanDrunen
Last modified: Tue Apr 8 16:44:43 CDT 2008