Jeremy D. Miller -- The Shade Tree Developer

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics

There's been a bit of chatter today on the altdotnet list about whether to use the CAB, another framework, or roll your own.  It seemed like a good time to get off my duff and start writing some new installments in the Build your own CAB series.  When I wrote an earlier installment on the Event Aggregator pattern, Udi Dahan left a suggestion in a comment for a more general usage of the pattern.  Lo and behold, I found a reason to apply his suggestion to StoryTeller and came away pretty happy with the results.  Coincidentally, CodeBetter-mate Glenn Block is writing some posts on the very same Event Aggregator pattern with a similar design.  I'll use this to pressure him into finishing his series;-)

 

What's the Problem?

Ideally a single screen is basically self-contained.  The View and Presenter talk to each other, the Presenter talks to some sort of service layer, and that's that.  But like any idyllic spot, the real world will intrude.  A screen will often need to react to actions or events taking place in other parts of the UI.  You may have a feature that broadcasts updated data from the server to the client that would need to update the display of a screen or the state of the UI.  Some action taking place in one screen might affect other parts of the UI, and it can easily be more than one element of the UI. 

At this point we're facing a situation where a lot of screen elements, Views, and pieces of the application shell are interrelated and need to be synchronized.  The problem we have right off the bat is how to get all of these pieces talking together in the first place.  The next problem is how to avoid making the UI code a tangle of references from all these different parts trying to get a reference to each other just to talk to one another.  If you're not careful, you can create some nasty spaghetti code connecting all the disparate parts of the UI.

Once again, we're going to reach for a Publish/Subscribe method for routing events from the origin of the event.  To eliminate undesirable coupling between the event publishers and the event listeners we're going to route all the notifications through a central messaging hub called an Event Aggregator.  If a publisher can simply send a message to the Event Aggregator hub, the hub will relay that message to the proper listeners.  New listeners can be added later to the Event Aggregator without the publishers being affected. 

 

The Scenario

Here's my scenario from StoryTeller.  I have a pane on the left that acts in the same capacity as the solution explorer from Visual Studio to show all of the open testing projects and provide access to all of the Tests and Test Suites.  When a user adds a new testing project to the working set, another part of the UI called the FixtureExplorer needs to update its display to reflect the available Fixture classes for each project.  This isn't coded yet, but when a testing project is removed, I need all open tabs that are related to that project to be closed and removed from the UI.   

The three main challenges I faced were

  1. How does the Event Aggregator know which listeners are interested in a given message?
  2. How to get a reference to the Event Aggregator?
  3. How to register the listeners?

 

Message Routing with Generics

I didn't want my listeners to have to implement a generic handler method that would have to analyze a message coming in to decide what to do.  For the sake of easier testing and more explicit code, I wanted a strongly typed method as part of a listeners public API that handled a specific message.  Furthermore, to simplify the listeners, I want the Event Aggregator to be responsible for deciding what messages a given listener is interested in.  And oh by the way, I have to be able to add new message types without changing the Event Aggregator.

In the end, the answer was really pretty simple.  The listeners just need to implement the IListener<T> interface, where T is the type of a message.  The Handle() method is where a listener reacts to an event message. 

    public interface IListener<T>
    {
        void Handle(T subject);
    }

For a concrete example of a listener, I have a class called FixtureExplorerPresenter that needs to listen to several events related to testing projects. To register as a listener, it just has to implement IListener for each event:

    public class FixtureExplorerPresenter : IFixtureExplorerPresenter,
                                            IListener<SystemLoaded>,
                                            IListener<SystemActivated>,
                                            IListener<SystemDeactivated>,
                                            IListener<SystemReloaded>,
                                            IListener<SystemRemoved>

The listening methods look something like this:

        // This is the implementation of the IListener<SystemLoaded> interface
        // The Event Aggregator will call this method when a SystemLoaded message
        // is published
        public void Handle(SystemLoaded subject)
        {
            ISystemUnderTest system = subject.SystemUnderTest;
 
            ReloadSystem(system);
 
            if (isSystemActive(system))
            {
                loadNode(system);
            }
        }

The details of what it's doing isn't that interesting, but I want to point out how easy it was in a listener to subscribe to an event.  There's no generic method that receives an event and then has to run it through a switch statement to figure out what to do.  FixtureExplorerPresenter just gets a SystemLoaded event and works accordingly.  In my unit tests for FixtureExplorerPresenter I can just walk right up to the class and call Handle(SystemLoaded) without having to set up anything of the rest of the system.

        [Test]
        public void SystemLoadedReloadsTheFixturesForThatSystemAndResetsTheViewIfThisIsTheActiveSystem()
        {
            FixtureExplorerPresenter presenter = _mocks.CreateMock<FixtureExplorerPresenter>(_view, _writer);
 
            ISystemUnderTest system = ObjectMother.SystemUnderTest();
            presenter.ReloadSystem(system);
 
            Expect.Call(_view.ActiveSystemName).Return(system.Name);
            SystemFixturesNode theNode = new SystemFixturesNode(system, new FixtureToken[0]);
            Expect.Call(presenter.GetNode(system)).Return(theNode);
            _view.DisplayFixturesNode(theNode);
 
 
            _mocks.ReplayAll();
            presenter.Handle(new SystemLoaded(system));
            _mocks.VerifyAll();
        }

Creating the listeners was easy enough because all a listener had to do was implement IListener<T> interfaces for all the events it needed to listen to.  We've still got an outstanding responsibility for publishing events to the correct listeners, so let's turn our attention to the hub in our publish/subscribe eventing model.

In StoryTeller, events are published to the rest of the system by getting a reference to an instance of this interface (highly elided) shown below;

    public interface ITestEventPublisher
    {
        ...
 
        void PublishEvent<T>(T subject);
        void AddListener(object listener);
        void RemoveListener(object listener);
    }

A client of the ITestEventPublisher interface just needs to call the PublishEvent<T>(T message) method to broadcast a message to the rest of the application.  In one of the samples above I showed some code in the FixtureExplorerPresenter class that responded to a "SystemLoaded" event to update part of the screen.  Here's the other side of the publish/subscribe.  Another part of the system called ApplicationController is responsible for loading new testing Projects into the test explorer.  The ApplicationController.AddProject() method shown below needs to tell FixtureExplorerPresenter and the other listeners that a new "SystemUnderTest" has been loaded into the UI.  Instead of calling FixtureExplorerPresenter directly and causing the n^2 messaging anti-pattern, it sends a SystemLoaded message to an ITestEventPublisher.

        public virtual void AddProject(IProject project)
        {
            ISystemUnderTest system = project.BuildSystem();
            _hierarchyNode.AddProject(project, system);
 
            // _publisher is an instance of the ITestEventPublisher interface
            _publisher.PublishEvent(new SystemLoaded(system));
        }

The concrete method PublishEvent() is shown below: 

        public void PublishEvent<T>(T subject)
        {
            // _listeners is just an ArrayList of all the Listeners 
            foreach (object listener in _listeners)
            {
                // Determine if a Listener handles the message of type T
                // by trying to cast it
                IListener<T> receiver = listener as IListener<T>;
                if (receiver != null)
                {
                    // I'm using SyncronizationContext to handle moving processing
                    // from a background thread to the UI thread without having 
                    // to worry about it in the View or Presenter
                    _context.Send(delegate { receiver.Handle(subject); }, null);
                }
            }
        }

Connecting a message to the correct listeners is actually pretty simple.  The TestEventPublisher object as an ArrayList of all the possible listeners.  When it receives a message of type SystemLoaded, TestEventPublisher:

  • Loops through each listener
  • Checks if each listener implements the interface IListener<SystemLoaded>
  • If it does implement that interface, it calls Handle(SystemLoaded) on the listener

That's it.  Assuming that the TestEventPublisher "knows" about all the listenters, and a client of ITestEventPublisher "knows" how to get to the single instance of TestEventPublisher, event aggregation is easy.  So let's talk about getting the pieces together.

 

Getting Listeners and Publishers Together

The easy part is just getting a reference to the single instance of the ITestEventPublisher.  That's just a little bit of vanilla StructureMap configuration:

            ForRequestedType<ITestEventPublisher>()
                .TheDefaultIsConcreteType<TestEventPublisher>().AsSingletons();

The bolded part just directs StructureMap to only create one shared instance of ITestEventPublisher.  Anything that needs to get at an ITestEventPublisher can just service location by a good ol'fashioned call to:

            ITestEventPublisher eventPublisher = 
                ObjectFactory.GetInstance<ITestEventPublisher>();

if you only need a transient reference to ITestEventPublisher.  The mass majority of the time I just use constructor injection like my ApplicationController class:

        public ApplicationController(
            ICommandExecutor executor, 
            IContentPanel panel, 
            IProjectRepository repository,
            ITestEventPublisher publisher, 
            IHierarchyNode hierarchyNode)
        {
            _executor = executor;
            _panel = panel;
            _repository = repository;
            _publisher = publisher;
            _hierarchyNode = hierarchyNode;
        }

I do create ApplicationController from StructureMap, so all of the constructor arguments are located and passed into the constructor by StructureMap.  All of the Command and Presenter classes that would need to broadcast event messages are generally created by StructureMap anyway, so the constructor injection method turns out to be pretty simple.

All that's left is to make sure that the listeners are correctly registered with the TestEventPublisher, and that's a little bit trickier.  Before I start, I should say that this is just the way that I did this and not THE way to do this.  In StoryTeller there are two different categories of listeners.  There is one set of objects like the FixtureExplorerPresenter that are driving pieces of the application shell at all times.  These objects are started as the application loads and registered with TestEventPublisher right off the bat.  I'm using the fluent interface mechanism for configuring StructureMap in code as a more or less internal DSL (I refuse to argue with you if it's actually a DSL or not).  One of the advantages of an internal DSL written in just plain C# is that you can extend it at will or wrap it with other calls.  I created a little wrapper around the method to register a type with StructureMap called Start():

 

        private static List<Type> _startables = new List<Type>();
 
        protected CreatePluginFamilyExpression<T> Start<T>() where T : IStartable
        {
            Type type = typeof (T);
 
            // Just track which Types need to be started and registered with
            // ITestEventPublisher as the application is loaded
            _startables.Add(type);
 
            // Continue on with StructureMap configuration as usual
            return ForRequestedType<T>();
        }

In the StructureMap bootstrapping, I just call Start() to denote a type that needs to be started right off the bat and registered with the event aggregator.  Notice that our old friend FixtureExporerPresenter is one of the "Startables."

            Start<IFixtureExplorerPresenter>()
                .TheDefaultIsConcreteType<FixtureExplorerPresenter>().AsSingletons();
 
            Start<IApplicationController>()
                .TheDefaultIsConcreteType<ApplicationController>().AsSingletons();
 
            Start<ICommandExecutor>().TheDefaultIsConcreteType<CommandExecutor>().AsSingletons();

 

Part of the application bootstrapping is a call to this little routine.  It just iterates through the "Startable" types, gets that type from StructureMap, and registers each by calling AddListener() on the event aggregator.

        private static void attachListeners()
        {
            ITestEventPublisher eventPublisher = 
                ObjectFactory.GetInstance<ITestEventPublisher>();
 
            foreach (Type type in _startables)
            {
                IStartable startable = (IStartable) ObjectFactory.GetInstance(type);
                startable.Start();
 
                eventPublisher.AddListener(startable);
            }
        }

I also register the Presenter for each screen as a listener each time a new screen is opened.  I'll talk much more about this in a future post, but for right now let's say that all screens are opened by a single class (sometimes it's the ApplicationController, other times it's what I call a ScreenCollection for a lack of a better name).  By making all screen activation and deactivation go through a common point it's now easy to perform bookkeeping operations on the screens.  In the case of StoryTeller, I do this in the method that's called to add a new screen to the main docking manager (the code is in ContentPanel if you're playing along at home in the StoryTeller code):

 

        public void OpenScreen(IPresenter presenter)
        {
            _publisher.AddListener(presenter);
 
            ...Other stuff that adds the screen to the docking manager
        }

I mentioned above that I don't use WeakReferences.  To avoid a severe memory leak in the app, I need to remove a presenter from the publisher's list of listeners whenever a screen is permanently closed.

 

        public void Close(ContentTab tab)
        {
            bool needToActivateDifferentScreen = tab.Equals(TabControl.SelectedTab);
 
            IPresenter presenter = tab.Presenter;
            presenter.Deactivate();
            _publisher.RemoveListener(presenter);
 
            _tabControl.Items.Remove(tab);
 
            if (needToActivateDifferentScreen && _tabControl.Items.Count > 0)
            {
                ContentTab lastTab = getLastTab();
                TabControl.SelectedTab = lastTab;
            }
        }

If you're not going to track the elements that need to be removed from the publish/subscribe registry, or just don't want to do this, I'd recommend using WeakReferences inside your Event Aggregator class.

Other Issues

Some last little thoughts. 

  • I'm using SynchronizationContext for threading synchronization so that I don't have to worry about events coming from a background thread.  I've found that choking the background callbacks through just a couple different points can keep the rest of your code cleaner from the monotonous InvokeRequired()/Invoke() code. 
  • I don't think it matters in StoryTeller, but with other applications you may not want to immediately respond to an event if the screen is hidden or in an inactive tab.  You'll need more logic in these cases to delay an update activity.

 There's probably other issues, but I want to hit "publish" now.  Just throw in questions and I'll see what I can do.


Posted Fri, Jan 11 2008 1:35 PM by Jeremy D. Miller

[Advertisement]

Comments

The Build Your Own CAB Series Table of Contents - Jeremy D. Miller -- The Shade Tree Developer wrote The Build Your Own CAB Series Table of Contents - Jeremy D. Miller -- The Shade Tree Developer
on Fri, Jan 11 2008 1:38 PM

Pingback from  The Build Your Own CAB Series Table of Contents - Jeremy D. Miller -- The Shade Tree Developer

Rob wrote re: Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics
on Fri, Jan 11 2008 3:58 PM

I think this is a really elegant way to solve the problem.  I'm not sure why it didn't occur to me to do it like this before.  It must have been obvious to Udi, as this is a very common pattern in his area.  There is one thing I'm not sure of.  How would you handle the case of a "dataless" event?

Jeremy D. Miller wrote re: Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics
on Fri, Jan 11 2008 4:59 PM

Rob,

I didn't do a very good job of explaining that the "T" in IListener<T> is the subject of the message.  You would just make a type called:

public class ThisJustHappened{}

and go PublishEvent(new ThisJustHappened())

to tell an IListener<ThisJustHappened>() to call it's Handle<ThisJustHappened>() method.  In this case, all the message type is good for is routing

Nathan wrote re: Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics
on Fri, Jan 11 2008 5:17 PM

This looks simple but clever. How would you compare this to injecting a list of listeners into your publisher via IoC ?

Jeremy D. Miller wrote re: Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics
on Fri, Jan 11 2008 5:21 PM

@Nathan,

I'm not sure that you could get away with injecting the listeners via IoC, or that you would want to.  Most of the listeners in StoryTeller wouldn't even get created until well after the publisher.  It wouldn't be quite as dynamic.

I guess my real answer is "don't know"

» Daily Bits - January 14, 2008 Alvin Ashcraft’s Daily Geek Bits: Daily links plus random ramblings about development, gadgets and raising rugrats. wrote &raquo; Daily Bits - January 14, 2008 Alvin Ashcraft&#8217;s Daily Geek Bits: Daily links plus random ramblings about development, gadgets and raising rugrats.
on Mon, Jan 14 2008 8:10 AM

Pingback from  &raquo; Daily Bits - January 14, 2008 Alvin Ashcraft&#8217;s Daily Geek Bits: Daily links plus random ramblings about development, gadgets and raising rugrats.

Rob wrote re: Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics
on Tue, Jan 15 2008 9:36 AM

Thought you might find this interesting:

mdavey.wordpress.com/.../ui-automation

Reshef Mann wrote re: Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics
on Sun, Feb 24 2008 4:15 AM

Is using the EventPublisher the way to communicate between presenters?

Let me be clearer, say I have a file editor with a text editor component and a file browser tree which holds the tree of scripts I have. I have a presenter for each of there components. Should the message that tells the text editor presenter that the user wants to open a file (doubleclicks it in the tree) arrive by the event publisher?

Thanks, Reshef

Jeremy D. Miller wrote re: Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics
on Sun, Feb 24 2008 6:16 AM

@Reshef,

That's *a* way to do that.  In a very similar scenario, I had the Command that was fired off of the TreeNode just tell the ApplicationController to launch a new instance of the screen Presenter.

Kent Boogaart wrote re: Build your own CAB: Extensible Pub/Sub Event Aggregator with Generics
on Thu, Feb 28 2008 6:10 PM

Hey Jeremy,

Apologies again for the late reply - I'm slowly catching up on your excellent series on my bus trips to and from work. After reading this post I felt like putting together my own version of how I feel event aggregation could work. Basically, I made the event aggregator interface strongly typed (including the subscribe methods) and moved reflection usage into the auto-wiring code, where I feel it belongs. Also, publishes don't need to implement an interface - they just use the event aggregator service.

Perhaps we should IM on this or something because I'm really keen to get your thoughts and the code is probably too long to post here. Basically, the usage looks like this:

public class MyClass : ISubscriber<SomethingHappened>

{

   public void Receive(SomethingHappened subject)

   {

       //something happened

   }

}

public class SomeOtherClass

{

   private IEventAggregator _eventAggregator = ...;

   public void SomeMethod()

   {

       _eventAggregator.Publish(new SomethingHappened());

   }

}

Anyways, let me know.

Regards,

Kent

Jeremy D. Miller -- The Shade Tree Developer wrote Build your own CAB: The Main Players in the Composite Application Neighborhood
on Sat, Mar 8 2008 2:22 PM

I spent a week in Redmond in January taking part in a focus group on the vision for the P&amp;P&#39;s

Wo Kann Man Ein Auto Nicht Kaufen - PS3 Forum wrote Wo Kann Man Ein Auto Nicht Kaufen - PS3 Forum
on Sat, Mar 15 2008 5:18 PM

Pingback from  Wo Kann Man Ein Auto Nicht Kaufen - PS3 Forum

Jeremy D. Miller -- The Shade Tree Developer wrote Interception capabilities of StructureMap 2.5
on Wed, Jan 21 2009 7:15 AM

Reposted from structuremap.sourceforge.net/Interception.htm . I think this will be the last StructureMap

Community Blogs wrote Interception capabilities of StructureMap 2.5
on Wed, Jan 21 2009 7:24 AM

Reposted from structuremap.sourceforge.net/Interception.htm . I think this will be the last StructureMap

Pro Information Center » Blog Archive » Interception capabilities of StructureMap 2.5 wrote Pro Information Center &raquo; Blog Archive &raquo; Interception capabilities of StructureMap 2.5
on Tue, Jan 27 2009 8:54 AM

Pingback from  Pro Information Center  &raquo; Blog Archive   &raquo; Interception capabilities of StructureMap 2.5

Kazi Manzur Rashid's Blog wrote Use Event Aggregator to make your application more extensible
on Thu, Mar 5 2009 9:35 AM

Recently, in KiGG / DotNetShoutout we have integrated Twitter , nothing complex, very basic thing like

VS2010学习 wrote Use Event Aggregator to make your application more extensible
on Tue, Jun 9 2009 1:23 PM

Recently, in KiGG / DotNetShoutout we have integrated Twitter , nothing complex, very basic thing like

Jeremy D. Miller -- The Shade Tree Developer wrote Braindump on the Event Aggregator Pattern
on Tue, Jul 21 2009 10:22 PM

I’m working up my chapter on the “Event Aggregator” pattern for my book this evening

new ThoughtStream("Derick Bailey"); wrote Understanding The Application Controller Through Object Messaging Patterns
on Tue, Dec 22 2009 10:06 PM

Earlier in the year, I posted a few times on the Application Controller pattern that I was implementing

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Devlicio.us