How to do Control Flow via Polymorphism
Let's say you have n objects implementing an interface. Call these message, implementing
IMessage. Each message may cause a different action when given to another object, the receiver. Sounds like the perfect scenario for Polymorphism. Create a method
Handle() for each of the n messages specifying the concrete type in the signature. Now, let's say you receive that message from a MessageFactory and, obviously, that factory hands you the message via the interface. So here's the pseudo code:
Now, how do you pass this message off to the appropriate
Handle() version? You can't just call
Handle() with your message because you message is actually an
IMessage not the concrete type. I can think of three workarounds and don't like any of them. Is there another way, or is this just a limitation of C#? In the meantime, here are the three ways I can think of solving this:
The traditional way, use
switch to examine the incoming object and dispatch it. Just create a method
Handle(IMessage msg) and have it dispatch each to the proper
Handle(). Traditional, simple. But as n increases it becomes less and less readable and dispatch is now a runtime decision. It could just fail, if you overlooked a concrete class.
At runtime examine the actual type of the message and then inspect your receiver to dynamically invoke the appropriate methods. No worries about maintaining the code or readability. It's just magic... but runtime magic which may yet fail if you overlooked a concrete class.
There is probably a named pattern that goes with this approach. It shares some aspects with the Visitor pattern (that's what inspired me actually), but I don't know what this pattern is actually called. The idea is that each message doesn't get passed to the receiver but passes itself. I.e. it calls the appropriate method. For this to work, we add a method to our interface:
This works perfectly. After getting the message from the factory, you just call
msg.Dispatch(receiver). Since each message implements its own dispatch for itself, we get compile time checking. Yay. But we basically copy and paste the
Dispatch() method into every message class and we can't factor it into a common base-class, because that would break the flow control again. So it achieves the ends I'm after, but I don't like the implementation.
What other options are there?