Message Passing 2

As a follow-up to my previous post on message passing in a plug-in framework, I thought I'd post my solution to the problem. Now that I've finally reached a solution, that is...!

[Note: It would be worth reading the first post to get an idea of what I was trying to do. ]

[img_assist|nid=26|title=|desc=|link=node|align=none|width=480|height=318]

As you may recall, I was investigating a number of ways of passing messages in my plug-in framework, and was trying to settle on the built-in .NET event system as the underlying mechanism. Whilst discussing this with my friend and colleague Steve Harrison, he suggested that I look into the INotifyPropertyChanged interface provided as part of the .NET System.ComponentModel namespace.

Despite my earlier misgivings about using events in this way, this turned out to be very useful! Rather than having to declare my own event types, etc., I simply implement the interface as follows:

public class Plugin : IPlugin, INotifyPropertyChanged
{
// Implement IPlugin to identify this as a plugin
...

// Implement INotifyPropertyChanged to publish events
public event PropertyChangedEventHandler PropertyChanged;
}

Easy-peasy — one line of code!

Now, ideally, listening plug-ins would then subscribe directly to the events published by other plug-ins, but there's no way of doing this in a de-coupled framework. How does a listening plug-in know what events are available for subscription, or which plug-ins are publishing events?

To get around this, I introduced IMessageBroker (and an associated implementation):

public interface IMessageBroker
{
IDictionary> Events { get; }
void Initialise(ILog logger);
void RegisterEvents(IPlugin publisher, EventInfo[] publishedEvents);
void DeregisterEvents(IPlugin publisher, EventInfo[] publishedEvents);
void Subscribe(IPlugin publisher, EventInfo e, Delegate handler);
void Unsubscribe(IPlugin publisher, EventInfo e, Delegate handler);
IList GetSubscribedEvents(IPlugin subscriber);
}

As you can see, the message broker serves registration-type requests (from plug-ins publishing events) and subscription-type requests (from plug-ins listening to events). The EventInfo class is provided as part of the Reflection feature in C#/.NET, and does exactly what it says on the tin: it provides access to metadata on the event, and can mediate subscriptions. Using Reflection, a plug-in passes an array of the events it publishes to the message broker, which then stores it in a dictionary (or map for the Java-types out there) of events, indexed by publisher. Subscribe takes a publisher, an event and an event handler and creates the publish/subscribe relationship between the two plug-ins using the AddEventHandler method.

Handily, the broker is flexible enough to be able to subscribe any PropertyChangedEventHandler delegate to registered events — it doesn't have to be part of a plug-in. This was doubly useful when testing:

public void SendMessageTest()
{
// Subscribe to the event with a test handler
PropertyChangedEventHandler propertyChanged =
new PropertyChangedEventHandler(PropertyChangedHandler);
mb.Subscribe(eventPlugin, mb.Events[eventPlugin][0], propertyChanged);

// Fire the event
...

if (!handlerRun)
{
Assert.Fail("PropertyChangedHandler not run");
}
}

public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
Trace.WriteLine("PropertyChangedHandler was successfully called");
Trace.WriteLine(String.Format("Arguments passed were: {0}, {1}", sender, e));
handlerRun = true;
}

So, to summarise and conclude. The approach I took was a little more complex than might usually be the case in a publish/subscribe model, but this was dictated by the nature of the system. The use of the INotifyPropertyChanged interface greatly simplified the implementation of the events, and Reflection allowed me to dynamically pass around information about events rather than having to explicitly state it. The INotifyPropertyChanged pattern also gives me a model on which to base any extension to the pattern that I might need later on.

How easy it will be to introduce a hierarchy or taxonomy of events at a later stage remains to be seen; I don't think it will be particularly easy. However, my needs are fairly simple at this stage, and although the plug-in framework is intended to be generic, it's still tied to the personal finance program I'm trying to build. One of my goals is to completely decouple the framework from the program, but we shall see how easy this is. With the message passing problem solved, I now need to catch up on lost time.

Comments

The INotifyPropertyChanged interface is a big part of object the data binding world which with .NET 2.0 really has come alive (I was never a fan of data binding directly to a database). There are also a lot of other really good classes and interfaces for data binding which can be really useful even when not implementing data binding. I regularly use BindingList<T> instead of List<T> because of the extra functionality it has, especially when I'm working with WinForms.

You should have a look at Data Binding with Windows Forms 2.0: Programming Smart Client Data Applications with .Net by Brian Noyes – it's a great book and covers lots of good things such as the INotifyPropertyChanged interface and has some nice examples as well.

Rather helpfully the Automatic Class Tester will also pick up on the INotifyPropertyChanged interface on a class during unit testing and test it for you as well. Sweet!

This does look interesting, thank you. Having looked through BindingList, I'm not sure it's quite what I'm looking for. It will undoubtedly come in handy later on though, particularly when it comes to the GUI work.

The thing is, it now looks like I need to extend my message passing beyond the basic "this property has changed" notification. For example, I want to pass specific messages to register a plug-in with an abstract factory, so that I can create (for example) category objects from a particular category plug-in. Currently, I have to instantiate the entire category plug-in for each category, which seems plain silly. Additionally, what happens when I need to define yet another type of message to be passed? Would BindingList help me with this problem?

Maybe I could re-use some of the interfaces BindingList implements (such as IRaiseItemChangedEvents) in conjunction with INotifyPropertyChanged or my expanded equivalent to hopefully make life a bit simpler.

By the way, have you ever made use of NMock? It's brilliant :-)

-- Alastair

Add new comment