Methods
[Agent practical 3 of 9]
The final thing we need is to fix the appropriate access control methods for our instance variables.
We have a number of instance variables we've either set to private, or could set to private. These include:
--in Environment.java----------------------------------------------
int width = 300;
int height = 200;
double[][] data = new double[height][width];
along with specific values within data;
--in Agent.java----------------------------------------------------
int x = 50;
int y = 150;
-------------------------------------------------------------------
It's unlikely we'd want to get hold of, or change, any of the other variables.
With this in mind, make the above variables private, and write set and get methods for all of them, in the appropriate classes. Remember that useful code can be found on the Key Ideas page for the last lecture.
It is worth recalling that in the second practical we identified a potential issue with making a width
and height
variable
within the Environment class: someone might try to set them thinking this changed the array size. Here we have an opportunity to fix this. If we
set both variables to private
, but only provide getWidth()
and getHeight()
methods, there will be no way
to change them. Note also that it might be good if our setData()
method updated the size of the height
and width
variables
when a new dataset is loaded, thus:
public void setData(double[][] data) {
this.data = data;
height = data.length;
width = data[0].length;
}
Don't forget to supply a set and get method for a single location in the Environment data array as well. Here we have a slight issue that it is worth highlighting.
In general most Java methods, when they take in a width and height, or an x and y, do so in the order: width or x, height or y. However, because of the
way the data is going to be read from a file and displayed on screen later on, we need our data array to be the other way around: world.data[y][x]
, and
data = new double [height][width]
. This is a *right* pain, and just the kind of thing that's likely to result in errors somewhere down the line.
The solution is to be massively consistent, and isolate all treatments of the array in the order [y/height][x/width]
to within the Environment class, making
sure that all method calls work as usual for Java (x/width, y/height)
. This is the time to make sure this is set up perfectly. Here, then is
the set method for a single location in the Environment data array:
public void setDataValue(int x, int y, double value) {
data[y][x] = value;
}
Notice how the x and y swap order between the method call (which would be world.setDataValue(x,y,255.0);
) and the subsequent
treatment of the array (data[y][x] = value;
). Any other methods we write dealing with this array need just as careful thought.
Can you write the equivalent "getDataValue", for example?
If you now compile the model files, you'll get a great many debugging messages saying that variables now can't be accessed. Go through each and change the direct access to a method call, so, for example, in Agent.java, this:
double randomNumber = Math.random();
if (randomNumber < 0.33) x--;
if (randomNumber > 0.66) x++;
} while ((x < 0) || (x > world.width - 1));
this.x = x;
would become this:
double randomNumber = Math.random();
if (randomNumber < 0.33) x--;
if (randomNumber > 0.66) x++;
} while ((x < 0) || (x > world.getWidth() - 1));
this.x = x;
Note that there's no need to use set and gets for variables in the same class, so:
this.x = x;
stays as it is, rather than becoming:
setX(x);
or
this.setX(x);
Also, remember that:
if (world.data[y][x] > 10) {
becomes:
if (world.getDataValue(x,y) > 10) {
and similar for the setDataValues
. See, told you it would be a pain -- keep your arrays rectangular, rather than square, and issues
with this should leap out.
Once that's all working, check the code compiles and runs, and we're done for this practical.
Although it may seem like we've just been shuffling things around, we've practiced a number of key skills, including:
- making a constructor;
- using a constructor to tie two object classes together;
- making a method;
- making accessor and mutator methods;
- protecting variables from direct access.
In addition, we now have a full Agent-Based Model; one in which the behaviour is cleanly embedded in the Agent class, and the Model class concentrates on running the model.