Inheritance
[Agent practical 4 of 9]


Next, we'll look at the new Agent interface.

Here it is:

Download it to your practical directory and open it in Notepad++.

You should be able to see that on the one hand, the interface is extremely simple -- it's just empty stubs for the methods that should be implemented. Yet, on the other hand, the fact that we could do this kind of thing in Model.java:

private Agent[] agents = new Agent[numberOfAgents];

for (int i = 0; i < 5; i++) {
   agents[i] = new Nibbler(world);
}

for (int i = 5; i < agents.length; i++) {
   agents[i] = new Predator(world);
}

for (int i = 0; i < numberOfIterations; i++) {
   for (int j = 0; j < numberOfAgents; j++) {
      agents[j].run();
   }
}

makes the use of interfaces incredibly powerful in terms of keeping models flexible.


So, the interface only really defines something that runs and reports where it is. The reporting is just so we can keep track of the agents (and, ultimately, draw them on the screen). All of this is sufficiently generic that any given agent is probably going to need to do it. Given this, we can extract all this into an relatively generic class that does these jobs for us and implements the interface. This is what "Animal" does. Here it is:

Download it to your practical directory and open it in Notepad++.

Notice a few things about this class.

Firstly, note it implements the interface Agent.

Secondly, its run method doesn't actually do anything. Now, we could have declared the class abstract and the run method as one that subclasses had to provide themselves, however, its quite useful early in model development to have a relatively simple agent class you can build (unlike the interface) but that just acts as a placeholder while you get the rest of the model working. So, you can make objects of this class, but they don't do a lot. Any subclass will probably supply its own run method, which will override this one.

Thirdly, note that the class supplies the basic elements of storage and reporting we want. It can be set up with a copy of the Environment using its constructor, and has a default x and y, with the associated accessor methods dictated by the interface.

Finally, note that the instance variables are set as protected. This means they are unavailable to all external classes, except classes that subclass Animal. This means our Nibbler class will be able to directly access x, for example, rather than having to call getX() as it would if x was set to private in this superclass.


Once you're happy you understand this, let's move on to look at our Nibbler class.