“Latching” an Event Aggregator Subscriber

This is an excerpt from my book draft (written, oh, 10 minutes ago) about how to deal with having a hidden screen temporarily disregard messages from the Event Aggregator while it is hidden.  I do know that the spacing is wacky, and I apologize in advance.  Some quick terminology:

  1. Screen Conductor – This is what I’m tentatively calling the pattern for a class that acts as a mediator between the various screens in the application and controls the screen activation lifecycle.  In some apps it would just be the Application Controller, in other apps you might want to pull it into a separate class as the Application Controller gets big
  2. Screen Activator – Until I or someone else comes up with a better name, this is the responsibility for activating, deactivating, or tearing down a single screen.  I’ll write much more about this early next week.

 

For performance reasons you may want to have subscribers temporarily ignore events from the Event Aggregator.  A couple years back I worked on an application that presented visualizations of market data inside a financial reporting application.  The application would show different views of the same market data in different tabs within the application shell.  The market data would occasionally update on the backend and trigger the user interface to update.  The problem was that the act of performing the data visualization was quite expensive – and making the user wait while hidden screens were also updated probably made the application frustratingly sluggish to use.  In order to improve the user experience, the designers of this application “latched” the inactive screens so that they would not automatically update for new data until they were later displayed when the user selected other tabs.

So let’s say you are building a system with a similar scenario where screens need to potentially refresh the visualization of some sort of market data when the market moves. 

    // Subscribers would receive the “Market Data Changed”

    // event with this interface

    public interface IMarketDataListener

    {

        void Update(MarketDataChanged message);

    }

How would you go about latching the reaction to the MarketDataChanged event when a screen is hidden?  The first thing I think I would do is worry about how the individual screens “know” whether they are shown or not.  This is a case where I think you really want to be using the combination of Screen Conductor and the Screen Activator [LINK] to be managing the activation and deactivation of screens.  Let’s say that we’ve adopted a very simple implementation of these patterns:

    public interface IScreen

    {

        void Activate();

        void Deactivate();

    }

 

    public class SimpleScreenConductor

    {

        private IScreen _currentScreen;

 

        public void ActivateScreen(IScreen newScreen)

        {

            _currentScreen.Deactivate();

            newScreen.Activate();

            _currentScreen = newScreen;

        }

    }

The Activate() and Deactivate() methods “tell” an individual IScreen that it is turned on or off in the visual display and give each IScreen a chance to start or stop screen behaviors.  When a new screen is activated or an existing screen in the application is made the active screen, the SimpleScreenConductor will first tell the currently open screen to deactivate itself, and then activate the newly active screen.

Now that we have a way to let our market data visualization screens know when they are actively shown or hidden, we need to latch the market data changed events in the inactive case – with one wrinkle.  When the use does activate the inactive visualizations later, those screens need to refresh themselves for the new data.  A sample implementation of a LatchedPresenter is shown below:

    public class LatchedPresenter : IScreen, IMarketDataListener

    {

        private readonly IMarketDataRepository _repository;

        private bool _needToRefresh;

        private bool _latched;

 

        public LatchedPresenter(IMarketDataRepository repository)

        {

            _repository = repository;

        }

        public void Activate()

        {

            _latched = false;

            if (_needToRefresh)

            {

                updateVisualization();\

                _needToRefresh = false;

            }

        }

 

        public void Deactivate()

        {

            _latched = true;

        }

 

        public void Update(MarketDataChanged message)

        {

            if (_latched)

            {

                _needToRefresh = true;

            }

            else

            {

                updateVisualization();

            }

        }

 

        private void updateVisualization()

        {

            // call to the IMarketDataRepository to get

            // the new data and refresh the visualization

            // of the market data

        }

    }

 

When the LatchedPresenter receives a “MarketDataChanged” event, it first checks to see if it is “latched.”  If the events are latched it simply remembers that it needs to update itself upon the next activation by setting the field _needToRefresh to true.  If it is not latched, LatchedPresenter will update the visualization immediately.  Likewise, when LatchedPresenter is activated again it will check the _needToRefresh field.  If that field is true LatchedPresenter will update the visualization.

The performance benefit is obvious.  With latching the hidden screens are not consuming computing resources to update visualizations that might not ever be viewed by the financial analyst using the application. 

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Presentation Patterns. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/members/jmiller/default.aspx Jeremy D. Miller

    @Chris Wright,

    Cool, thanks for the comment.

  • Chris Wright

    I’ve encountered situations in which latching can reduce the number of calls to update the UI by an order of growth (O(N**2) to O(N), or O(N) to O(1)). While proper incremental updates would have helped, latching sometimes helps more, and it’s simpler to write.

  • http://kentb.blogspot.com Kent Boogaart

    > From UX perspective, it seems that it would be better to defer visualization

    I’d say it depends on a number of things, including:

    1. How many screens you have doing this. The combined cost of all those screens calculating “just in case” they’re displayed could kill the UX.
    2. How often these updates are likely to occur, and how many are likely to be unnecessary due to the view being hidden. For example, if the updates occur frequently and views tend to stay hidden for long periods of time, it would seem rather wasteful to do these background calculations.

    Anyway Jeremy, the core of your message is clear: views can defer refreshing if they are hidden by using a latch.

    Kent

  • http://ayende.com/Blog Ayende Rahien

    Jeremy,
    Recalculating is only a tad better than a frozen app.
    And if you can do recalculating, you can do the work in a background thread.
    In most cases, even with complex visualizations, most of the cost isn’t in the actual drawing, anyway.

  • http://codebetter.com/members/jmiller/default.aspx Jeremy D. Miller

    @Ayende,

    Let’s pretend we’re not idiots and put up a friendly “recalculating” message. There’s no way to paint the view in a background thread, but you *could* at least massage the raw data to create a Presentation Model in a background thread. You could also throttle the updating to show a few records at a time.

    Sigh. I’ll throw that on my backlog of topics.

  • http://ayende.com/Blog/ Ayende Rahien

    Isn’t there a hidden cost to it, though?
    When you are showing the screen, you are going to “freeze” until the visualization is ready.
    From UX perspective, it seems that it would be better to defer visualization on a hidden screen, but still preform it on the background.
    At least, assuming that you have events streaming to you in any sort of reasonable rate.