My question is about implementing different behaviours for different messages in an as extensible way as possible. I am aware of the visitor pattern, I am aware of double-dispatch, but I can't seem to figure out a solution, which satiesfies me (not within the limits of java at least).
My situation is as follows:
I have a hierarchy of Messages:
and a hierarchy of router-interfaces, each defining a route method for its own message-type:
which I would like to implement similar to this:
to be able to add and remove the capability to route certain messages, as well as to change routing-strategies for certain messages easily.
The problem is, that without switch-casting my message, which I don't want to do, I cannot select the respective function for the interface, because something like
CompositeRouter comp = new AllRouter(...//new Router instances);
MessageBase msg = new DerivedMessage();
msg.process(comp);
will lead to java selecting the overload <runtime message-type>.process(Router)
at compile time, which, at runtime, is invoked for the respective router object. So I cannot select the right calls to process() at compile time it seems. I can also not do it the other way round, because comp.route(msg)
will be resolved to <dynamic router-type>.route(MessageBase)
.
I could write a visitor, which selects the proper method from CompositeRouter, but therefor I would have to define the visitor interface with the respective route-Methods defined for all the MessageTypes up front, which kind of defeats the purpose, because it means that I have to rewrite the visitor whenever I add a new DerivedMessage.
Is there a way to implement this such that both Message and Router are extensible or is it hopeless given the current java-features?
Edit 1:
Something I forgot to mention is that I have 4 or 5 other situations, which are pretty much the same as the Router
-hierarchy, so I kind of want to avoid Reflection for method-lookup, because I am afraid of the runtime-cost.
Response to comments:
@aruisdante's assumption regarding @bot's suggestion is correct. I cannot Override, because I would loose the runtime-type of MessageBase, if I override route(MessageBase).
@aruisdante and @geceo: I know that I can do that - this what I meant with "switch-casting" (MessageBase has a MessageType field) - but I have like 11 actual message classes and ~6 locations in code where I need it, so it would be a HUGE pain implementation- as well as maintenance-wise.