Context: I believe that object creation and management in Java has a cost that we should bear in mind while programming. However, I don't know how big that cost is. Hence my question:
I have multiple functions that share the same arguments:
detectCollision(ArrayList<Mobile>, ArrayList<Inert>, double dt)
updatePositions(ArrayList<Mobile>, double dt)
- etc.
As I see it, there are two ways to organize them (see code below):
- define (possibly static, but not necessarily) methods and forward the arguments for each call
- create a temporary object with private member variables and remove argument list.
Note that the Mover
object has no private internal state and is just a bunch of algorithms that use the arguments ArrayList<Mobile>
, ArrayList<Inert>
, double dt
.
Question: Which approach is the prefered one ? Does it have a cost ? Is there a more standard alternative ?
Here is a snippet illustrating the first point:
public class Mover{
public static void updatePositions(ArrayList<Mobile>, double dt){...}
/* remove the static keyword if you need polymorphism, it doesn't change the question */
public static Collisions detectCollision(ArrayList<Mobile>, ArrayList<Inert>, double dt){...}
//etc.
}
Here is a snippet illustrating the second point:
public class Mover{
public Mover(ArrayList<Mobile>, ArrayList<Inert>, double dt){...}
public void updatePositions(){...}
public Collisions detectCollision(){...}
//etc.
private ArrayList<Mobile> mobiles;
private ArrayList<Inert> inerts;
//etc.
}
I'd recommend you to go with the second variant. Besides the good readability it will also allow you to extend the class later (see SOLID -> open / closed principle). In general, I'd never create such utility classes, as it is not OOP (OOP Alternative to Utility Classes).
While I think that static utility methods are not necessarily a Bad Thing™, you should be aware of the semantics of these methods. One of the most important points here is that static
means that a method may not take part in any form of polymorphism. That is: It may not be overridden.
So regarding the design, the second option offers a greater degree of flexibility.
A side note: Even if you choose the second approach, you might eventually dispatch to a (non-public) static method internally:
class Mover {
private final List<? extends Mobile> mobiles;
public void updatePositions(){
MyStaticUtilityMethods.updatePositions(this.mobiles);
}
}
Note that I'm not generally recommending this, but pointing it out as one option that may be reasonable in many cases.
It might be off-topic, but there is another degree of freedom for the design here. You could (and at least should consider to) go one step further: As far as one can guess (!) from the method names, you might consider having interfaces PositionUpdater
and a CollisionDetector
. Then you could store instances of classes implementing these interfaces inside your Mover
class, and dispatch the actual call to these. This way, you can easily combine the different aspects of what comprises a "Mover".
(I know, this does not answer the actual question. In fact, it just "defers" it to whether the PositionUpdater
should receive the data as arguments, or receive them at construction time...)
You could then assign instances of different implementations of the PositionUpdater
interface to a Mover
instance. For example, you could have concrete classes called LinearMovementPositionUpdater
and RandomWalkPositionUpdater
. Passing instances of these classes (which are both implementing the PositionUpdater
interface) to the Mover
allows you to change one aspect of the implementation of a Mover
- basically, without touching any code! (You could even change this at runtime!).
This way, the responsibilities for
- updating the positions
- detecting the collisions
are clearly encapsulated, in view of several of the SOLID principles that already have been mentioned in another answer.
But again: This is just a hint. Judging whether this approach is actually sensible and applicable in one particular case is what software engineers get all the $$$ for.
The problem you're having here is mostly due to having an orchestrated model (and which probably has an anaemic domain model to accompany it).
If instead you use an event driven approach these methods would simply disappear and be replaced by event handlers each of which would respond to the appropriate events independently of the other, taking whatever information they need from the initial event.
This means that you wouldn't have to deal with passing parameters or working out which parameters to pass -- each 'handler (method)' would know what it needs and would take it, rather than having to have an external orchestrator understand what data is needed and pass it in.
The orchestrator model breaks encapsulation by having it 'know' about the information needs of the components it is orchestrating.
Now, this isn't a java specific problem. It applies to most modern object-oriented languages. It doesn't matter if you're doing java, c-sharp, or c++. It's a general pattern.
To break away from this thinking, read about DDD (Domain Driven Design) and Event Sourcing.