Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I've been working with the Observer pattern in JavaScript using various popular libraries for a number of years (YUI & jQuery). It's often that I need to observe a set of property value changes (e.g. respond only when 2 or more specific values change). Is there a elegant way to 'subscribe' the handler so that it is only called one time? Is there something I'm missing or doing wrong in my design?

share|improve this question
    
Update 2014-12-9: Thanks for all the great answers! I've recently been using Backbone Models. When a set is invoked with multiple properties the model triggers a change event. Listeners can inspect the changed property for a hash of the properties changed. This approach essentially solves my problem. –  Kreegr Dec 10 at 2:03

4 Answers 4

up vote 1 down vote accepted

Yes. For this I returned to the original "Gang of Four" book on Design Patterns, then did some research online.

Mediator - Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. It is responsible for coordinating interactions for a group of objects.

State - Allow an object to alter its behavior when its internal state changes.

Here is an example of JavaScript mediator. Skip to the bottom for usage. Other examples may or may not be more relevant for you. But it exemplifies the notion.

"Instead of using the Observer pattern to explicitly set many-to-many listeners and events, Mediator allows you to broadcast events globally across colleagues." — Paul Marcotte

Here is some code:

//A State Object is simple encapsulation of runtime activity potentially embedded in Mediator.
var interactionState = { //record state, perhaps array, look up function, or even boolean flag.}

//A Mediator Adds and Removes components, and Broadcasts Events.

//A Mediator loops through added objects and references state to determine if action should be taken.

    for (var c in components) {
        if (typeof components[c]["on" + e] == "function")

          //Add in state comparison
          // if ( interactionState...) //is ready to fire action.
            //{ fire action }

          {
            try {
                //debug("Mediator calling " + e + " on " + c);
                var s = source || components[c];
                components[c]["on" + e].apply(s, a);
            } catch (err) {
                debug(["Mediator error.", e, a, s, err].join(' '));
            }
        }
    }

Your implementation of the "apply" behavior is probably different, and may be simplified.

Is there a elegant way to 'subscribe' the handler so that it is only called one time? 

I wouldn't think of it as 'limiting the events'. There is probably no real performance gain by doing this anyway. The perspective above, is to introduce a decision mechanism that says, "We have had 2 or more objects firing events, lets do this special task".

The Mediator encapsulates the decision mechanism, the state object encapsulates the runtime accumulation of information. Keep in mind that, at some point the state may need to be reset. This cleanup behavior would be sensible to be placed within the Mediator.

It may very well be the case that you can have your Observer listening for published events, then calls the Mediator to review the State, which makes the determination of interaction, if it is relevant, then broadcasts event with instruction to execute their special behavior.

EDIT: Also check out Evented Views. This is an exceptional article on the topic.

share|improve this answer

The Reactive Extensions framework was designed to address this issue for .NET event handlers. You may be interested in seeing how RX can let you respond to sequences of events, as described in the "Sequencing Events" section of this blog post. Basically, if you are interested in responding only when the user types "ABC" (in that order), you can say:

IObservable<Key> keyPress = 
   Observable.FromEvent<KeyEventArgs>(Application.Current.RootVisual, "KeyUp")
      .Select(ev => ev.EventArgs.Key);

Func<Key, IObservable<Key>> pressedIs = 
   key => keyPress.Where(pressedKey => pressedKey == key);

Func<Key, IObservable<Key>> pressedIsNot = 
   key => keyPress.Where(pressedKey => pressedKey != key);

IObservable<Unit> abcPressed =
   from firstKeyPressEvent in pressedIs(Key.A)
   from secondKeyPressEvent in pressedIs(Key.B).Take(1).Until(pressedIsNot(Key.B))
   from thirdKeyPressEvent in pressedIs(Key.C).Take(1).Until(pressedIsNot(Key.C))
   select new Unit();

abcPressed.Subscribe(()=> Debug.WriteLine("ABC was pressed."));

RxJs is the javascript implementation.

share|improve this answer

One technique (using Event-Based Systems with a Mediator):

I take advantage of the Mediator Pattern quite a bit -- one way to control event-flow is to instantiate different Mediator objects per-module, and pass only a specific Mediator to each module (as a, or in a sandbox) -- this will allow each module to only communicate through that specific medium, only when you say it should -- and, with this method, when you broadcast messages, you won't be broadcasting on the entire system / medium that the ApplicationDirector is listening on. An oversimplified implementation might look something like this:

// ApplicationCore / Mediator
var mediator = new Mediator();

var media = {
    authentication: mediator.installTo({}),
    ui: mediator.installTo({}),
    clients: mediator.installTo({})
};

// ... pass each medium to the given module

function broadcast(event, data) {
    for (var medium in media) {
        media[medium].fire(event, data);
    }
}

broadcast('command://start', null);

media.ui.on('action://form/sumbit', function (data) {
    media.authentication.fire('action://form/sumbit', data.email);
    media.clients.fire('action://form/sumbit', data.name.first, data.name.last);
});

// UI Module
sandbox.mediator.on('command://start', function () {
    // ...
    sandbox.mediator.fire('action://form/sumbit', formData);
    // ...
});

// Authentication Module
sandbox.mediator.on('command://start', function () {
    // ...
    sandbox.mediator.on('action://form/sumbit', function () { /* authenticate */ });
    // ...
});

// Clients Module
sandbox.mediator.on('command://start', function () {
    // ...
    sandbox.mediator.on('action://form/sumbit', function () { /* add new client */ });
    // ...
});

Now, you can use a Mediator to handle when exactly certain changes (e.g. quantity) have an effect on the rest of the system. You even have more control over when a given module should notify the system of the change(s). Furthermore, if you're developing a very large-scale application -- using a Mediator that implements an EventHub is a good approach.

There are plenty of odd things about this example, but I hope you find this useful for what it demonstrates.

share|improve this answer
    
Yes - A Mediator is a great way to manage event flow. My question was more specifically, at the lowest level, wondering how/when to synthesize an event that's based on 2 or more concurrent other events. A Mediator, would still be required to do this. –  Kreegr Dec 10 at 1:58
    
Ah... I think I see. You mean something like an approach to avoiding callbackHell because the context involves the combination of two events. If this is the case, and the events occur between two different modules, you may use some sort of Fly Weight -- update a counter or push to an array on it, but basically use an appDirector to check this property's value, say, ['myFirstEvent', 'mySecondEvent'] and fire some sort of 'bothEventsFired' event to kick off the target-process. You also may want to check out Object.definePropert-y/ies get &set as you could watch batch changes with this. –  Cody Dec 10 at 18:21

I don't know, if this idea exists already and if it has a name - if so, it would be cool if anybody tell it to us :-)

You could add an object which

  • observes your two or three values,
  • is observed by your aim object and
  • only sends a message, if your condition is reached.

This sounds to me like an "conditional observer proxy".

share|improve this answer
4  
event-aggregator –  k3b May 21 '12 at 14:57
    
Thank you k3b - i love to hear, that this pattern exists already! +1 –  Sebastian Bauer Aug 6 '12 at 18:37
    
You mean something like KnockoutJS's .computed() property? ... That could work! –  Cody Dec 10 at 18:23

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.