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.
instanceof
with your constraints. Most generic message passing systems I've worked with in Java, for example Akka, have a single method similar to yourroute
, and SOP is toinstanceof
check the incoming messages and dispatch accordingly. You could provide some common functionality in your baseRouter
class to abstract this so that subclasses register callbacks for concrete classes to simulate the double-dispatch. If you're open to that I can provide an example as an answer. – Tirpitzinstanceof
. It's not pretty but it is simple. – Geyer