Reputation: 1934
I am designing a game and I have good overview of what I am doing.
However I've been trying to improve my OOP skills but now and then I face the same problem, how should I use the abstracted objects?
Lets say I have a list of Entitys that represents anything that has x and y property on screen and probably width and height haven't figured all out yet!
Then I have special types of entitys, one that can move and one that cannot and probably something like collidable in future.
They're all in a collection of Entitys (List<Entity>
in my case) and now I want to simulate entitys that moves and are instances of DynamicEntity on main loop but they're all on abstract list of Entitys and I don't know is the Entity in loop either dynamic entity or not.
I know I could just check it with instanceof
but I am pretty sure that's not the best idea..
I've seen some people having something like boolean inside the Entity for checking its type but I don't really want to hardcode all kind of entitys there..
I just want to know what is the best practice in such case?
Upvotes: 1
Views: 90
Reputation: 272367
You're right that you don't want to check the instance type or have some sort of function to check capability. My first question would be - why do you have a list of entities of that base type in the first place ? It sounds to me like you need to maintain a list of dynamic entities.
You could implement a move()
method that does nothing for non-dynamic entities, but again that doesn't seem right in this particular scenario.
Perhaps it would be better to implement an event that triggers the iteration of that list, and pass that event into each object in turn. The dynamic entities could decide to move upon that event. The static entities would obviously not.
e.g.
Event ev = ...
foreach(e : entities) {
e.actUpon(ev);
}
In this scenario you could have different event types, and the entities would decide upon their action upon the basis of the event type and the entity type. This is known as double-dispatch or the visitor pattern.
Upvotes: 2
Reputation: 33029
Usually it's better to avoid checking the type if possible. If you think you need to use instanceof
in your code then there's probably an abstraction you could be using to make your design more extensible. (If you decide to add a third type of Entity
in the future you don't want to have to go back and update all of your instanceof
checks with a third case.)
There are two common ways to have different actions based on an instance's concrete type without checking the concrete type:
One common way is the visitor pattern. The idea here is to create a Visitor class with an action for each type of object. Next, each concrete class has an accept
method which simply calls the correct visit
method inside the visitor class. This single level of indirection allows the objects to choose the correct action themselves rather than you choosing it by checking the type.
The visitor pattern is usually used for one of two reasons. 1) You can add new actions to a class hierarchy that implements the visitor pattern without access to the classes' source code. You only have to implement a new visitor class and use it in tandem with the visitable classes' pre-existing accept
methods. 2) When there are many possible actions one can perform on classes from some type hierarchy sometimes it's more clear to split each action off into it's own visitor class rather than polluting the target classes with a bunch of methods for a bunch of different actions, so you group them with the visitor rather than the target classes.
However, in most cases it's easier to do things the second way: to simply override the definition of a common method in each concrete class. The Entity
class might have an abstract draw()
method, then each type of Entity would implement that draw()
method in a different way. You know that each type of Entity has a draw()
method that you can call, but you don't have to know the details of which type of entity it is or what the method's implementation does. All you have to do is iterate over your List<Entity>
and call draw()
on each one, then they'll perform the correct actions themselves depending on their type since each type has its own specialized draw()
implementation.
Upvotes: 5
Reputation: 38255
If your processing of entities relies on knowing details about the entity types, then your Entity
abstraction doesn't buy you much (at least not in this use-case): your List<Entity>
is almost as opaque for you as a mere List<Object>
.
If you know that every entity you can imagine will be either static or dynamic, there's no "hard-coding" in having a boolean property to all entities: isDynamic()
or something.
However, if the dynamic aspect only makes sense for a subset of your entities, this flag will indeed bring some mess to your abstraction. In this case, my first guess is that you didn't model the use-case properly since you need to work with a list of items that do not provide enough polymorphic information for you to handle them.
Upvotes: 1