A couple of days ago I posted my code that models systems made up of moving parts. I got several great tips and ideas and here is my latest version. My actions are now methods annotated with @Action (instead of separate classes as in my original code). My Model
class contains a HashMap
of Class, ArrayList<? extends Agent>
so each ArrayList
contains agents of the same type.
In myModel.start()
method I get annotated methods for each class and invoke them on each ArrayList
member. The code looks more promising than the original one, but my main concern is that I had to use reflection to invoke actions on each model agent as I read that reflection is costly.
I would also like to hear your opinions on my usage of generics as I am new to the concept.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GenericsTest
{
public static void main(String[] args)
throws NoSuchFieldException, SecurityException
{
Model machine = new Model();
ArrayList<Gear> gears = new ArrayList<Gear>();
ArrayList<Lever> levers = new ArrayList<Lever>();
int i;
for (i = 0; i < 1000; i++)
{
Gear gear = new Gear();
gear.setDiameter(i);
gears.add(gear);
Lever lever = new Lever();
lever.setLength(i + 2000);
levers.add(lever);
}
machine.addAgentList(Gear.class, gears);
machine.addAgentList(Lever.class, levers);
machine.start();
}
}
class Model
{
public void start()
{
for (Class<? extends Agent> clazz : listOfAgents.keySet())
{
List<Method> actions = new ArrayList<Method>();
for (Method m : clazz.getDeclaredMethods())
{
if (m.isAnnotationPresent(Action.class))
{
actions.add(m);
}
}
for (Agent agent : listOfAgents.get(clazz))
{
for (Method action : actions)
{
try
{
action.invoke(agent, new Object[]
{});
} catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e)
{
e.printStackTrace();
}
}
}
}
}
public Map<Class<? extends Agent>, ArrayList<? extends Agent>> getAgentLists()
{
return listOfAgents;
}
public void addAgentList(Class<? extends Agent> cls,
ArrayList<? extends Agent> agents)
{
listOfAgents.put(cls, agents);
}
private final Map<Class<? extends Agent>, ArrayList<? extends Agent>> listOfAgents =
new HashMap<Class<? extends Agent>, ArrayList<? extends Agent>>();
}
interface Agent
{
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
@interface Action
{
}
/*
* Agent classes
*/
class Gear implements Agent
{
private int diameter;
public int getDiameter()
{
return diameter;
}
public void setDiameter(int diameter)
{
this.diameter = diameter;
}
@Action
public void rotate()
{
System.out.println("rotating ... " + this.toString() + " "
+ this.diameter);
}
@Action
public void replace()
{
System.out.println("replacing ..." + this.toString() + " "
+ this.diameter);
}
}
class Lever implements Agent
{
private int length;
public int getLength()
{
return length;
}
public void setLength(int length)
{
this.length = length;
}
@Action
public void move()
{
System.out.println("moving ..." + this.toString() + " " + this.length);
}
}
I will also try improve my original FactoryClass
idea making Action
an interface and post the code in another post.
EDIT
Changed the code to improve my list declarations:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GenericsTest
{
public static void main(String[] args)
throws NoSuchFieldException, SecurityException
{
Model machine = new Model();
List<Gear> gears = new ArrayList<Gear>();
List<Lever> levers = new ArrayList<Lever>();
int i;
for (i = 0; i < 1000; i++)
{
Gear gear = new Gear();
gear.setDiameter(i);
gears.add(gear);
Lever lever = new Lever();
lever.setLength(i + 2000);
levers.add(lever);
}
machine.addAgentList(Gear.class, gears);
machine.addAgentList(Lever.class, levers);
machine.start();
}
}
class Model
{
public void start()
{
for (Class<? extends Agent> clazz : listOfAgents.keySet())
{
List<Method> actions = new ArrayList<Method>();
for (Method m : clazz.getDeclaredMethods())
{
if (m.isAnnotationPresent(Action.class))
{
actions.add(m);
}
}
for (Agent agent : listOfAgents.get(clazz))
{
for (Method action : actions)
{
try
{
action.invoke(agent, new Object[]
{});
} catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e)
{
e.printStackTrace();
}
}
}
}
}
public Map<Class<? extends Agent>, List<? extends Agent>> getAgentLists()
{
return listOfAgents;
}
public void addAgentList(Class<? extends Agent> cls,
List<? extends Agent> agents)
{
listOfAgents.put(cls, agents);
}
private final Map<Class<? extends Agent>, List<? extends Agent>> listOfAgents =
new HashMap<Class<? extends Agent>, List<? extends Agent>>();
}
interface Agent
{
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
@interface Action
{
}
/*
* Agent classes
*/
class Gear implements Agent
{
private int diameter;
public int getDiameter()
{
return diameter;
}
public void setDiameter(int diameter)
{
this.diameter = diameter;
}
@Action
public void rotate()
{
System.out.println("rotating ... " + this.toString() + " "
+ this.diameter);
}
@Action
public void replace()
{
System.out.println("replacing ..." + this.toString() + " "
+ this.diameter);
}
}
class Lever implements Agent
{
private int length;
public int getLength()
{
return length;
}
public void setLength(int length)
{
this.length = length;
}
@Action
public void move()
{
System.out.println("moving ..." + this.toString() + " " + this.length);
}
}
ArrayList<...>
reference types should be simplyList<...>
. But you still haveArrayList
s all over. \$\endgroup\$