I'm designing a game. In the game, various game objects extend different interfaces (and one abstract class) depending on what they need to be doing, and are passed to handlers which take care of items with a specific interface at defined intervals (they actually spread all their work out in a neat sort of way to make sure input/video/etc is always processed).
Anyway, some of these objects extend the abstract class Collider and are passed to a CollisionHandler. The Collider class and handler take care of everything technical involved in collision, and just ask that an object implement a collidesWith(Collider c) function, and modify itself based on what it has collided with.
Objects of many different classes will be colliding with one another, and will act very differently depending on what type of object they have collided with and its specific attributes.
The perfect solution seems to be to use instanceof like so:
class SomeNPC extends Collider{
collidesWith(Collider c){
if(c instanceof enemy){
Fight it or run away depending on your attributes and theirs.
}
else if(c instanceof food){
Eat it, but only if it's yellow.
}
else if(c instanceof BeamOfLight){
Try to move towards its source.
}
}
}
This actually seems like a legitimate place for instanceof. I just get this bad feeling. Like how if a goto made sense in some particular situation. Does the design feel fundamentally off to anyone? If so, what would you recommend doing to achieve the same behavior.
It's strange no one have posted a "not broken" visitor pattern implementation yet. And by not broken I mean not relying on the side effects of the visitor. To do that, we need our visitors to return some result (let's call it
R
):Next, we modify
accept
to accept the new visitors:the concrete implementations of collider will have to call the proper
visit
method, like this (I'm assumingFood implements Collider
, but this is not necessary):Now to implement the collisions we can do something like this:
You can see that the compiler can help you find all the places where you forgot to specify the behaviour. This is not a liability as some people claim, but advantage because you can always disable it by using a default implementation:
You will also want some way to reuse the code for collisions of (Food, SomeNpc) and (SomeNpc, Food), but this is outside the scope of this question.
If you think this is too verbose -- that's because it is. In languages featuring pattern matching this can be done in several lines (Haskell example):
This is a use of instanceof that will make most people cringe. Here is a useful link that may provide you with some insight: http://www.javapractices.com/topic/TopicAction.do?Id=31
Instead, a Collider should have some sort of collide() method that each subclass will override.
You can use a visitor pattern here where subclasses of Collider would implement a separate method for each type of collision it can encounter. So you method might turn into:
You get the idea. The strange thing in your model is that a Collider base class has to know about all potential subclasses in order to define a method for that type. Part of this has to do with the problem of the visitor pattern, but it's also because Collider is combined into Visitor. I'd suggest looking for a separation of the visitor and collider so you can define how you want to behave when collisions happen. What that means to your colliders is they can change how they behave to a collision based on internal state. Say they are invulnerable vs. normal mode, hidden, or dead. Looking at the client code it might be:
The visitor class is often recommended. With Visitor you implement a visit method:
But this is in fact equivalent to:
Both of these have disadvantages.
Sometimes the easiest way is to do:
It has the advantage that it keeps the knowledge of how an object reacts to things it hits with that object. it also means that if Balloon has subclasses RedBalloon, BlueBalloon etc we don't have to take that into account, as we would do with the visitor pattern.
The traditional argument for not using instanceof is that it isn't OO, and you should be using polymorphism. However you might be interested in this article: When Polymorphism Fails, by Steve Yegge which explains why instanceof is sometimes the right answer.
There are a few ways to handle it this:
Map<Class, CollisionHandler>
, you pick the CollisionHandler off the passed collider class (or enum type) and callprocessCollision(source)
. Each Colllider class has its own map of handlers.Map<Pair<ColliderType>, CollisionHandler>
for each new collision type you'd need to create a new handler. The plus side is that such declarations can be external (dependency injection), so new NPCs/Object can be added along with the Collision handlers.Anyways first you make sure it works the way you feel the most comfortable with and you can refactor it later.
This might be something to consider. Using
instanceof
can be useful but another way to consider the visitor idea is to build a Factory class that examines a mutual field to determine what type the visitor is.You still have to build new classes for each implementation, but they are routed in an abstract class that defines a method
The returned object could be the action .