4

I was going through old code and noticed one pattern that repeats itself all over the place - dynamic dispatch.

While I don't see any problem with the implementation itself, I was wondering if there are other ways of handling dynamic dispatch.

To bring an example - consider a scenario where at run time I'm going to get a message - either Customer or Visitor and perform some operation based on the type of the message. For the sake of brevity let's say all we need to do is to print out a corresponding message type.

So to go from words to code

public abstract class Message
{
   public abstract string Id {get;}
}

public class Customer: Message
{
   public override string Id {get {return "Customer";}}
}

public class Visitor: Message
{
   public override string Id {get {return "Visitor";}}
}

And handling part

public interface IHandleMessage
{
  bool CanHandle(Message message);
  void Handle(Message message);
}

public class CustomerHandler: IHandleMessage
{
   public bool CanHandle(Message message)
   {
      return message.Id == "Customer";
   }

   public void Handle(Message message) {Console.WriteLine(message.Id);}
}

public class VisitorHandler: IHandleMessage
{
   public bool CanHandle(Message message)
   {
      return message.Id == "Visitor";
   }

   public void Handle(Message message) {Console.WriteLine(message.Id);}
}

public class MessageHandlersFactory
{
   private static readonly IEnumerable<IHandleMessage> _messageHandlers =
       new List<IHandleMessage> {new CustomerHandler(), new VisitorHandler()};

   public static IHandleMessage GetHandlerForMessage(Message message)
   {
       return _messageHandlers.FirstOrDefault(h => h.CanHandle(message));
   }
}

And the client code looks like this

public class Program
{
   public static void Main(Message message)
   {
      IHandleMessage handler = MessageHandlersFactory.GetHandlerForMessage(message);

      handler.Handle(message)
   }
}

Now as I said, I don't mind this implementation, what I really don't like is this pattern repeats itself all over the place, which makes me think if this is not an overuse of dynamic dispatch.

To generalize - everywhere where decision has to be made based on incoming message/data/argument I do have more or less the same implementation as I provided above.

So my question is - given example above is there other ways of handling this kind of scenarios ? Or this is recommended and accepted way of dealing with dynamic dispatch?

Michael
  • 183
  • 1
  • 5
  • I've always been a fan of Anders Hejlsberg's "static wherever possible, dynamic when necessary" principle. – Mason Wheeler Feb 15 '17 at 20:11
  • @MasonWheeler so how does that apply to this one ? How can this be made static ? – Michael Feb 15 '17 at 20:38
  • Dynamic dispatch is one of the core features of object-oriented programming. Assuming that you're not simply anti-OO, is the real problem that your codebase is filled with two-line snippets that pick an object type and then immediately dispatch? – kdgregory Feb 16 '17 at 12:31
  • @kdgregory I'm not anti-OO, nor I dislike dynamic dispatch. Just when I see it all over the place in the code I start to question if it is being used properly or maybe it's being overused. What I was asking above was - is this the only way to handle dynamic dispatching or there are other ways (patterns) ? As an analogy when you need to create objects you don't just go with Factory - you may choose to use Builder pattern, Specification, something else.. I noticed that every time I need to execute some action based on the run time information **the same** approach is used. Hence my question. – Michael Feb 16 '17 at 13:09
  • 2
    This is type unsafe imitation of virtual call. In object oriented language there is a safe native support for this. – Basilevs Feb 16 '17 at 14:59
  • 1
    @Basilevs code sample would be really helpful to clarify your point in the context of this question. Can you post some? I assume you're suggesting objects handle themselves ? – Michael Feb 16 '17 at 15:53
  • 1
    I think you are really questioning the use of the visitor pattern which adds something like double-dispatch to languages that don't support it. It might help to clarify as single-dispatch and OO are somewhat inseparable. – JimmyJames Feb 17 '17 at 18:32

5 Answers5

4

Dynamic dispatch, aka polymorphism, is one of he core components of object-oriented programming. And in an application that uses polymorphic objects, I would expect to see dynamic dispatch used everywhere.

However, what I wouldn't expect to see is code that immediately creates an object, makes a single polymorphic call on that object, and then throws the object away. And if that's what's happening in your code, I'd say that your object model doesn't need to be polymorphic, and probably doesn't benefit from polymorphism.

To give an example of what I consider "good" polymorphism: all investments (stocks, bonds, options, &c) have a price, a quantity, and a value that is calculated from those two attributes. However, the specific calculation depends on the investment type, as do the rules for trading the investment. So it makes sense to define a base Investment interface, with concrete subclasses. This interface defines the behaviors and interactions that the program can have with a generic investment -- it is an abstraction.

On the other hand, you might define an object model for processing events in which there is no need for an abstraction: you read an event and then hand it off for processing, with each event being processed by a completely distinct set of code. In this case, there's little need for a common interface that defines a process method.

That doesn't mean that an object-oriented design isn't still useful: objects are a useful tool for modularization. But they don't necessarily need to be polymorphic, and your factory method could create the object and immediately call its process function..

And if you do need polymorphism, be aware that any alternative such as a map of functors is simply moving the implementation of dynamic dispatch into your code, rather than letting the runtime handle it.

kdgregory
  • 5,220
  • 23
  • 27
2

The goal of this is to ultimately pick a method to handle a message. You are currently emulating a Dictionary with CanHandle substituting for a key match. But for the conditions presented here, a standard dictionary will find the handler more efficiently.

When I've done this, instead of using an abstract class and a constant string message type, I've used a marker interface and the type itself as the message type. Something like this (from your classes above):

public interface IMessage { }
public class Customer : IMessage { }
public class Visitor : IMessage { }

This makes it pretty easy to define a dictionary-based class for handling any IMessage:

public class MainHandler
{
    private Dictionary<Type, Action<IMessage>> _handlers;

    // constructor
    public MainHandler(Dictionary<Type, Action<IMessage> handlers)
    {
        _handlers = handlers;
    }

    public void Handle(IMessage message)
    {
        Action<IMessage> handle = null;
        if (_handlers.TryGetValue(message.GetType(), out handle))
        {
            handle(message);
        }
        else
        {
            throw new NotImplementedException("No handler found");
        }
    }
}

Now the trick is to get the dictionary populated. You could do this using another marker interface for the handler, then find them with reflection. But I find it much clearer to manually list the handlers. That way it's easier to trace usage with standard tools, e.g. Find All References. You can also use helper functions to make it a little easier.

public class CustomerHandler
{
    public static void Handle(Customer message) { return; }
}
public class VisitorHandler
{
    public static void Handle(Visitor message) { return; }
}

public class Program
{
    // helper
    static KeyValuePair<Type, Action<IMessage>> ToHandle<T>(Action<T> action)
    {
        Type key = typeof(T);
        // build in cast to proper type to make it easier on handler code
        Action<IMessage> value = message => action((T)message);
        return new KeyValuePair<Type, Action<IMessage>>(key, value);
    }

    public static void Main()
    {
        // initialize things
        var mainHandler = new MainHandler(
            new List<KeyValuePair<Type, Action<IMessage>>>
            {
                // main stuff that changes here
                ToHandle<Customer>(CustomerHandler.Handle),
                ToHandle<Visitor>(VisitorHandler.Handle)

            }.ToDictionary(kvp => kvp.Key, kvp => kvp.Value));

        // probably happens in a loop
        IMessage message = ... ; // read message
        mainHandler.Handle(message);
    }
}

If you've made it this far, you're probably thinking static handler methods are not going to work because what about dependencies? But you can still provide dependencies to static methods by passing them in as parameters.

public class CustomerHandler
{
    public static void Handle(string setting, SharedResource res, Customer message) { ... }
}

public class VisitorHandler
{
    public static void Handle(string setting, Visitor message) { ... }
}

public class Program
{
    ...

    public static void Main()
    {
        // initialize things
        var res = new SharedResource();
        var setting = ConfigHelper.LoadSettingFromSomewhere();
        var mainHandler = new MainHandler(
            new List<KeyValuePair<Type, Action<IMessage>>>
            {
                ToHandle<Customer>(x => CustomerHandler.Handle(setting, res, x)),
                ToHandle<Visitor>(x => VisitorHandler.Handle(setting, x))

            }.ToDictionary(kvp => kvp.Key, kvp => kvp.Value));

        ...
    }

You could even create and pass in functions that you might need in the handler:

// initialize things
Func<SqlConnection> getConnection = () => new SqlConnection("...");
...
    ToHandle<Customer>(x => CustomerHandler.Handle(getConnection, ... , x));

In this implementation, the CanHandle boilerplate is eliminated. Handlers can be defined in any way you want (no interface to conform to). Then the wire-up gives you the opportunity to provide handlers with exactly the dependencies they need to fulfill their use case.

Kasey Speakman
  • 4,341
  • 19
  • 26
  • Thanks for detailed explanation of your point and the code! – Michael Feb 16 '17 at 08:46
  • This is awesome, how you did this dynamic wire-up, yet kept the handler static to let its parameters vary freely. I never considered an interface without methods/properties as a way to specify type without being constrained by the need to have method signatures matching. – Mike Feb 16 '17 at 15:09
  • @Mike Thanks. For the wire-up I used the functional programming concept [Partial Application](https://en.wikipedia.org/wiki/Partial_application). The marker interface, I picked up from CQRS examples. – Kasey Speakman Feb 16 '17 at 15:46
1

I think its popular because of its extensibility. But we should remember that at some point in your code you instantiate the Customer and Visitor objects.

You gloss over this in your example code, but it would be quite possible to have :

public class Customer
{
    public Customer(IHandler<Customer> handler)
    {
        this.handler = handler;
    }

    private IHandler<Customer> handler;
    public Handle() { handler.Handle(this);}
}
Ewan
  • 70,664
  • 5
  • 76
  • 161
0

One could get rid of the CanHandle() method and simply use a dictionary where the key is the message Id. That would be simple and clean.

If the handling logic becomes more complex or if multiple handlers can be invoked for a particular message, then CanHandle() serves a better purpose.

But it seems from your example that only a single handler for each message is needed, so a dictionary of handlers would suffice. The factory could use the internal dictionary and return the correct handler class based on key (id).

Jon Raynor
  • 10,905
  • 29
  • 47
  • Thanks for reply! So do you think this is the way to go (in terms of approach) every time there is a need for dynamic dispatch (whether through dictionary/internal dictionary)? – Michael Feb 15 '17 at 20:36
  • Yes it's simplier when only one handler is needed as one does not need all the CnaHandle() logic. As noted above, if multiple handlers can be returned, then the CanHandle() could be employed as dictionary matching is unique on the key (one or no items returned at most). – Jon Raynor Feb 15 '17 at 21:04
  • Yeah to be honest that's the variation I'm using - for simple cases it is a dictionary with some executable actions (I didn't post it to keep question short) or CanHandle/Handle pairs for more complex cases. I thought that's an implementation detail that can be left out in the question as my question was is this the way of handling dynamic dispatching - that is when you need to perform operation based on _something_ which is going to be known at run time. Now, judging by all 3 responses I'm more convinced that it is. – Michael Feb 16 '17 at 08:49
0

CanHandle and Handle can be combined into one method, returning true if handled.

What happens when two handlers are capable of handling the same message? Currently it will use the first one inserted into the list. This could be a source of differing behavior (i.e. bugs), if the handlers are dynamically created and inserted.

Object oriented programming environments often implement this type of dispatch in a simpler way:

  • Message identifiers are small integers, not strings.
  • Message handlers are function pointers (delegates in .net).
  • Dispatch consists of indexing an array of function pointers using the message identifier, finding the handling function, and invoking it.

You could use a similar algorithm.

Frank Hileman
  • 3,922
  • 16
  • 18
  • As you can see from example I posted, it is not a real world example rather something to make my question clear - few handlers capable of handling same message is not really a concern in this question's context. Probably my question was not clear enough - my main concern was if this is the way to handle dynamic dispatching or there are other ways because as I stated in the question everywhere this pattern is used and I was not sure if there are other means to handle dynamic dispatching. – Michael Feb 16 '17 at 08:45
  • 1
    @Michael I added a section explaining a common way dispatching is implemented in object oriented environments. – Frank Hileman Feb 17 '17 at 17:38