Reputation: 25
I'm working on a evolutionary simulation model implemented in Java and ran into a key object-orientation design issue which I can't seem to work out. The problem can be summarized as follows:
I have a base abstract class Player and two concrete subclasses, Signaller and Receiver:
abstract class Player
{
Strategy[] strategies;
double fitness;
...
}
class Signaller extends Player
{
double quality;
....
}
class Receiver extends Player
{
double[] weights;
int chosenChannel;
....
}
Now I need classes which represent collections of Signallers and Receivers and I am constrained to using arrays to store them. There are methods common to both population types, but also specific methods for a signaller populations or for a receiver population.
Conceptually, I would need something like this:
abstract class Population
{
Player[] members;
void mixUpPopulation() {...}
Strategy[] getMeanStrategies() {...}
double getMeanFitness() {...}
...
}
class SignallerPopulation extends Population
{
Signaller[] members;
...
}
class ReceiverPopulation extends Population
{
Receiver[] members;
double[] getChannelPreferences() {...}
...
}
I have thought of two basic ways of achieving this:
Have the class hierarchy as described above.
Problem: How can the Player[]
in the superclass and also the Signaller[]
or Receiver[]
in the subclasses refer to the same collection of objects?
Make the base class generic:
class Population <T extends Player>
{
...
T[] members = (T[])new Object[popSize];
}
Problem: How do I implement the methods specific to each of the population types?
I would appreciate your insights into those problems or maybe suggestions of other ways of tackling the problem.
Upvotes: 1
Views: 4295
Reputation: 25
I went with the design suggested by @Medo42 (his first option) and @LumpN. The only cast required was when setting the array, but that wasn't problematic. I am giving the outline of the code here, maybe someone will find it helpful.
abstract class Population
{
protected abstract Player[] getMembers();
protected abstract void setMembers(Player[] members);
void mixUpPopulation() {...}
Strategy[] getMeanStrategies() {...}
double getMeanFitness() {...}
...
}
class SignallerPopulation extends Population
{
Signaller[] members;
protected Player[] getMembers()
{
return this.members;
}
protected void setMembers(Player[] members)
{
this.members = (Signaller[]) members; //required cast
}
...
}
class ReceiverPopulation extends Population
{
Receiver[] members;
protected Player[] getMembers()
{
return this.members;
}
protected void setMembers(Player[] members)
{
this.members = (Receiver[]) members; //required cast
}
double[] getChannelPreferences() {...}
...
}
Upvotes: 0
Reputation: 3821
You can use the design 1 as in your question, but instead of storing an array in the abstract base class you add an abstract protected method (e.g. getMembers()) that will be implemented in the subclasses to return the actual array as an array of Players.
Alternatively, you can make the abstract base class generic, and derive the subclasses with the appropriate types:
abstract class Population<T extends Player>
{
T[] members;
void mixUpPopulation() {...}
Strategy[] getMeanStrategies() {...}
double getMeanFitness() {...}
...
}
class SignallerPopulation extends Population<Signaller>
{
public SignallerPopulation(int popSize) { members = new Signaller[popSize]; }
...
}
class ReceiverPopulation extends Population<Receiver>
{
public ReceiverPopulation(int popSize) { members = new Receiver[popSize]; }
double[] getChannelPreferences() {...}
...
}
Upvotes: 2
Reputation: 11
Remove the members
from Population
and add an abstract getter-method for members to it (public abstract Player getMember(int i)
and public abstract int getNumPlayers()
or something similar). Subclasses are required to implement the getter. This way you will still have access to the Player
part of XYPopulation
members in Population
.
Upvotes: 1