views:

329

answers:

6

I am building an AI for an RTS game. (For the Spring RTS engine, if anyone is interested.) The way I have set it up it consists mainly of a collection of Components that communicate by means of fired Events. Every component has a method handleEvent(Event) that receives events fired by the other components.

Event is an interface. A hierarchy exists, each subclass providing more detailed information on the the event they represent, which can be accessed by using the getter methods specific to that class. As an example, the class UnitGainedEvent implements the Event interface. This class has a subclass UnitFinishedEvent (which indicates construction of a unit or building is completed, for anyone that is curious.)

Not every component will be interested in all events, so I would like to let Components simply pick the events they are interested in and receive only those. Also, the set of possible events is extendable, so it is not a valid option to let Components have designated methods for each Event type. Initially I thought the visitor pattern might help me with this problem, but it fails because it also requires a fixed set of Event types. (I'm not 100% sure I understand the pattern correctly. Please do correct me if I'm wrong.)

So far, the only solution I have found is to implement each Component's handleEvent(Event) method something like this:

public int handleEvent(Event event)
{
    if (event instanceof UnitGainedEvent)
    {
        UnitGainedEvent unitGainedEvent = (UnitGainedEvent) event;
        // things to do if I lost a unit
    }

    else if (event instanceof UnitLostEvent)
    {
        UnitLostEvent unitLostEvent = (UnitLostEvent) event;
        // things to do if I lost a unit
    }

    // etc.
}

However, I don't really like having to cast the events to the specific Event classes. Now, remembering that method overloading can be used to invoke different methods depending on the runtime type of a parameter, I quickly came up with a brilliant solution, elegant as it was simple: I could create a base class with an empty implementation of handleEvent(Event), and simply have subclasses receive the events they are interested in by creating a method handleEvent(UnitGainedEvent unitGainedEvent), for example. Wanting to make sure it would work I set up a quick test case:

public class Main
{
    public static void main(String[] args)
    {    
        handleEvent(new UnitGainedEvent(null));
    }

    public static void handleEvent(Event event)
    {
        System.out.println("Handling generic Event");
    }

    public static void handleEvent(UnitGainedEvent event)
    {
        System.out.println("Handling UnitGainedEvent");
    }
}

And to my great satisfaction, this code actually prints 'Handling UnitGainedEvent.' So I set about implementing.

My Component base class looks like this: (Well, not really. This is the Component class stripped of everything not relevant to the problem I'd like to demonstrate.)

public class Component
{
    public void handleEvent(Event event)
    {
        System.out.println("Handling Event");
    }
}

And this is an example of a subclass:

public class SomeComponent extends Component
{
    public void handleEvent(UnitGainedEvent unitGainedEvent)
    {
        System.out.println("Handling UnitGainedEvent");
    }   
}

In order to test the setup, I use the following main class:

public class Main
{
    public static void main(String[] args)
    {
        Component component = new SomeComponent();
        component.handleEvent(new UnitGainedEvent(null));
    }
}

So I run the code, and to my great surprise, the result is a neatly printed 'Handling Event.' Interestingly enough, if I change the type of the component variable to SomeComponent, it does print 'Handling UnitGainedEvent.' For some reason or another, the system blindly invokes the Component class' handleEvent(Event) method, instead if the overloading SomeComponent's handleEvent(UnitGainedEvent). (I would be interested in hearing Sun's reasoning behind this, thought it isn't really relevant to my question - not like they'll fix it just because a handful of people would find it a very useful feature.)

Scouring the net tells me that other people have run into the same problem. Very few people, from the minute amount of information I find, but people nonetheless, although I find more information on general method overloading and overriding than I ever wanted to know. However, in the end, I fail to find a solution.

Now, my (fairly obvious) question is, is there some way to get around this problem? Failing that, can anyone think of, or help me find, another solution that is equally convenient?

Edit: I can hardly believe I have answers after only ten minutes. I'm pleasantly surprised. :) However, most of the answers so far suggest in one form or another that I make one seperate method for each possible Event type. Technically this is possible, but this would require me to go back into the code and add a new method every time someone comes up with a new Event type. Which I've learned is bad coding practice. (Plus, I already have some 20+ Event types, and I'm not even done yet.) Instead, I would rather do use the solution involving casting, as described above. At least that way I can assure that unknown event types are simply ignored, leaving me free to only handle those events where I want to use them. I'm really hoping for a solution that combines the best of both however. No casting, and no going back into my code for each event type.

Many thanks in advance, Loid Thanead

A: 

So I run the code, and to my great surprise, the result is a neatly printed 'Handling Event.' Interestingly enough, if I change the type of the component variable to SomeComponent, it does print 'Handling UnitGainedEvent.' For some reason or another, the system blindly invokes the Component class' handleEvent(Event) method, instead if the overloading SomeComponent's handleEvent(UnitGainedEvent).

Overloading doesn't really work that way. You need to invoke the overloaded method on the subclass, not the superclass. Since you're referencing the Component type, Java doesn't know that you've overloaded the method in a subclass, it only knows if you've overridden it or not.

Do you really need the generic handleEvent(Event) class? Can you instead write handleEvent() methods for the specific Event subclasses without having a handler for the Event superclass?

Kevin
A: 

In the example above you are overloading the method from the base class, but you are not overriding it. It would work as expected if you defined an abstract (or empty) method handling the UnitGainedEvent in your Component class.

You might want to consider attacking the problem from a different angle. One approach you could take is to do something similar to what is done in Swing with all kinds of listeners. You could group the events logically and provide a number of listeners for those events. Within each group you can then provide an adapter so that the clients can override them handling only specific events they are interested in.

Bogdan
A: 

It looks like you're looking for covariant arguments, which are not supported in Java (covariant return types are, however).

Why not use the observer pattern? If a component is always interested in events of a particular type, you can create a static registry and make sure that all interested components are notified whenever an event of that type is fired.

zweiterlinde
A: 

The visitor pattern is the correct solution. Basically what you need is multiple dispatch. You want to select a method based on both the type of component and the type of event which is not possible in Java. Take a look at this wikipedia article for more information: [http://en.wikipedia.org/wiki/Multiple_dispatch#Java][1].

[1]: http://Multiple Dispatch

Alex Rockwell
A: 

For some reason or another, the system blindly invokes the Component class' handleEvent(Event) method, instead if the overloading SomeComponent's handleEvent(UnitGainedEvent). (I would be interested in hearing Sun's reasoning behind this, thought it isn't really relevant to my question - not like they'll fix it just because a handful of people would find it a very useful feature.)

The compiler is "stupid". Change your code like this and you will see why it has to be:

public class Main
{
    public static void main(String[] args)
    {
        Component component = new SomeComponent();
        Event     event     = foo();

        component.handleEvent(event);
    }
}

foo() return some sort of event, no idea what. Since the compiler cannot know what subclass is returned and if the Component supports that method all it can do is work with what it knows - that the Component class has a handleEvent that takes an Event.

For some of wht you are doing you should look at the java.util.Event and java.util.EventObject types.

TofuBeer
A: 

Use reflection to get your desired effect. When you dispatch an event to a listener, look at its methods to find a best-match (what you were hoping the compiler would do for you).

You would obviously want to cache the invocation targets or else performance could get painful.

Travis Jensen