views:

148

answers:

1

i want to dispatch messages to specific handlers through a common message processor

//
// Library code
//

abstract class Processor<M extends MessageHandler<? extends Message>> {
    HashMap<Class<Message>, M> handlerMap;
    void addHandler(M, Class<Message>);
    void run() {
        while(true) {
            ...
        }
    }
    // QUESTION - how to define this to include the fact that H extends M<T>
    //            actually im just trying to avoid the ugly cast in the client code.
    abstract <H extends MessageHandler<T>, T extends Message> void dispatch(H handler, T message);
}

class MessageHandler<T extends Message> {
}

class Message {
}

//
// Client code
//

class ServerMessage extends Message {
    ...
}

class ServerMessageHandler<T extends Message> extends MessageHandler<T> {
    ...
    void process(T msg, Object... params) {
        ...
    }
}

class ServerProcessor extends Processor<ServerMessageHandler<? extends Message>> {
    @Override
   <H extends MessageHandler<T>, T extends Message> void dispatch(H handler, T message) {
        // QUESTION - how do i get rid of this cast?
        ((ServerMessageHandler<T>)handler).process(T, ...);
   }
}

the server processor will be processing many different server messages, all with their own subtypes, members, etc. each one of these messages will have a separate handler. some base message classes will share handlers.

my question is how do i avoid that ugly cast in the client code? i cant seem to write the signature of the dispatch method to include the facts that we know the message handlers will be of type M (ServerMessageHandler), and that the particular ServerMessageHandler is parameterized by T, and a message of type T will be in the arguement list.

EDIT

i dont mind if the addHandler method cannot get total type safety, i can do some runtime checks to make sure the proper relationships are enforced ( i would have to change its signature to do that properly though ). my main goal here is to somehow enforce (through the signature) the two relationships in the dispatch method. that the handler being called is of type M, and that it is parameterized by T. to actually call this method there will be some unchecked casts in the run method (which in turn calls dispatch). but i dont mind having the ugliness there. just trying to move it out of the ServerProcessor.

+3  A: 

Processor.dispatch can take any type which extends MessageHandler.

The method in ServerProcessor isn't a complete override Processor.dispatch - it will fail for handlers which aren't ServerMessageHandler instances with a class cast exception ( I'm assuming that ServerMessageHandler not extending MessageHandler is a typo rather than by design; otherwise it will fail for all inputs as no MessageHandler is a ServerMessageHandler; otherwise just make the parameter's type ServerMessageHandler<T> ).

Why would you expect there to be a way of expressing intrinsically unsafe behaviour in a typesafe manner?

The contract for Processor.dispatch is that H can be any MessageHandler and T can be any message. If H instead can only be the type of handler Processor is parametrised by, M, then use that in the definition:

abstract class Processor<M extends MessageHandler<? extends Message>> {
    ...
    abstract <T extends Message> void dispatch (M handler, T message);
}

But again, this loses something in that M isn't related to T. There isn't anything equivalent to the unbind/bind idiom in Java, and it does seem that the dispatch method either shouldn't care about the subtype of message, or the processor should care - you seem to be maintaining a mix of message handler types by your addHandler method taking a handler for any method at runtime, then wanting to make it specific for a particular type in the dispatch method.

So does a processor handle only one message type or not? If it does, and you want type safety, then make the message type a type parameter. If it handles multiple message types decided at runtime, then you won't get compile time checking of the type.


You can move the cast if you separate the event processing loop and the mechanism to dispatch to a handler:

/**
* @param <M> message type
*/
class Processor < M > {

    Dispatcher<M> dispatcher;

    public Processor ( Dispatcher<M> dispatcher ) {
        this.dispatcher = dispatcher;
    }

    void run ( M...messages ) {
        for ( M message : messages ) {
            // as there is no mechanism in java to get from Class<T> to Foo<T>, this call
            // must be made with the wildcard H Foo<?>
            dispatcher.dispatch ( message );
        }
    }
}

interface Dispatcher<M> {
    <T extends M> void dispatch ( T message );
}

class Message {
}

class ServerMessage extends Message {
    //...
}

interface ServerMessageHandler<T extends ServerMessage> {
    //...
    void process ( T msg, String param ) ;
}

class ServerDispatcher implements Dispatcher<ServerMessage > {
    HashMap < Class < ? extends ServerMessage >, ServerMessageHandler<?> > handlerMap = new 
    HashMap < Class < ? extends ServerMessage >, ServerMessageHandler<?> > ();

    <T extends ServerMessage >
    void addHandler ( ServerMessageHandler<T> handler, Class < T > clz ) {
        handlerMap.put ( clz, handler );
    }

    @SuppressWarnings("unchecked")
    // cannot use a trick like clz.cast() as we want ServerMessageHandler<T> rather than T
    <T extends ServerMessage> ServerMessageHandler<T> getHandler ( Class < ? extends ServerMessage > clz ) {
        return ( ServerMessageHandler<T> ) handlerMap.get(clz);
    }

    @Override
    public <T extends ServerMessage>
    void dispatch ( T message ) {
        ServerMessageHandler<T> serverMessageHandler = getHandler ( message.getClass() );

        serverMessageHandler.process ( message, "wibble" );
    }
}

however, if the loop is driven from a queue of the base event type as in the Process.run() code, it will be no better in terms of type safety, as the only version of ServerDispatcher.dispatch called is with T = ServerMessage, and the cast is hidden in the getHandler() method. Safety comes from the symmetry of addHandler and getHandler, not from different bindings of the type variable T. Separating Processor and Dispatcher means only the specific Dispatcher has to know about the relationship between T and ServerMessageHandler<T>.

Pete Kirkham
your right about the typo. edited
aepurniet
it may be somewhat unsafe, but the heterogenous typesafe container pattern gave me a little hope that it could be done. im trying to get the exposed interface to be functionally typesafe, while underneath there are some casts to get it to work. im just trying to get these casts into the library side code.
aepurniet
fixed the typo in the addHandler method. also removed the unnecessary specialization in the ServerMessageHandler class, T should just extend Message.
aepurniet
I think you're just putting it in the wrong place - you don't need the cast to call the methods of MessageHandler on it; and you can't register any message handlers which aren't ServermessageHandlers, so what further requirements do you have?
Pete Kirkham
MessageHandler has no methods. each type of handler (like a ServerHandler) will have its own methods with its own parameter list. this is because only the ServerProcessor knows how to call the ServerHandler. the abstract Processor does not know what the handlers need to complete their jobs. (an ugly solution would be for the handlers to have the same interface and access everything they need to do their jobs through singletons, or some other global storage scheme). this is not particularly attractive if your running multiple Processors of the same type in the same jvm.
aepurniet
ServerProcessor is just one type of processor, i anticipate writing many different ones (like ClientProcessor, DBProcessor, etc). i omitted writing those in the code since it would be very repetitive. that whole ClientCode section will be implemented many times.
aepurniet
the processor handles many different massages, the message handler only handles one type of message. the processor is a really dispatcher that fowards messages to the proper handlers.
aepurniet
If MessageHandler has no methods, what's it for? You're just putting an arbitrary restriction on the handler implementations and gaining nothing. It seems you have four things - the message, the processor loop which pushes messages to the dispatcher, the dispatcher which knows about the extra arguments, and the handler. You could combine dispatcher and processor loop with C++ templates, but generics only give erasure to the parent type. Choosing a parent type with no methods means there's nothing you can safely do after erasure.
Pete Kirkham
i can call the dispatch method of the Processor subclass. that will know how to handle calling the `Handler` type that is parameterized by the message type. each type of handler will have a different method signature for its process method. your exactly right when you say im trying to simulate type specialization in c++, or message dispatching in objective-c. i dont mind having runtime errors, since those can be detected at `addHandler()` time (by generic type reflection and handled gracefully). im just trying to avoid that cast in the client code.
aepurniet
i guess we could consider handlers to handle things of type object. we can make messagehandlers an interface and then we dont have arbitrary restrictions, since its just a marker. this is not necessarily bad practice, and it happens all over the jvm library code.
aepurniet
You won't. There's no way to avoid the cast; in some cases you can use Class<T>.cast, but only to cast to T. but that doesn't avoid a cast, it only saves telling the complier that it's unsafe. If you know the cast won't fail, why not just add a comment to that effect and leave it? You could end up adding a lot of complexity, but no functional gain, and move the cast around, but as long as you receive messages of any type and select a handler at runtime you'll have a cast somewhere.
Pete Kirkham
yes! i realize i have to cast. im just trying to do the cast in the library section of the code, and not the client section. i just cant seem to write the signature of the abstract Processor.dispatch in a way that will eliminate the need for a cast in the implementation of that method. this may be because its not possible, but the typesafe container pattern (i've applied it elsewhere, but without the extra complications listed in the problem) gave me some hope of maybe this being possible to do. i want to do those casts in Processor.run instead (there are already a couple of casts in there).
aepurniet
aepurniet
thanks for the help, i dont think its possible to do without a cast in client code, (or full fledged <class<M>, handler<M>> associations (either fully or pass through) i was hoping to get the Processor to do).
aepurniet