Система оповещения модулей

Приятно наблюдать за тем как развиваются языки программирования, за появлением различных фреймворков и паттернов. Так в моем любимом JavaScript в последнее время появилась хорошая тенденция оформлять код в виде модулей (классов). Это обеспечивает и лучшую читаемость программ и повышает повторное использование кода.

Разделение функционала на отдельные модули требует от программиста обеспечить их коммуникацию между собой. Одним из способов обеспечения такой связи служит система оповещения модулей, заинтересованных в получении информации о каких-то событиях, происходящих в других модулях.

/**
 *  Observable Object
 *  Just call it in constructor of your class (delegate pattern)
 *  Ex: var me = Observable(); // create new object
 *      or
 *      Observable(me);        // extend existing object
 */
var Observable = function (target) {
    // callback holder
    var observers = {};
    // create new object if needed
    target = target || {};

    target.attachObserver = function (eventType, callback) {
        if (!observers[eventType]) {
            observers[eventType] = [];
        }
        observers[eventType].push(callback);
        return this;
    };

    target.detachObserver = function (eventType, callback) {
        var a, i;
        if (eventType) {
            if (observers[eventType] && callback) {
                a = observers[eventType];
                for (i = 0; i < a.length; i++) {
                    if (a[i] === callback) {
                        a.splice(i, 1);
                        break;
                    }
                }
            } else {
                delete observers[eventType];
            }
        } else {
            observers = {};
        }
        return this;
    };

    target.notify = function () {
        var a, i, handler, data = [];
        // copy notification arguments
        Array.prototype.push.apply(data, arguments);
        a = observers[data.shift()];
        if (a && a.length > 0) {
            for (i = 0; i < a.length; i++) {
                handler = a[i];
                if (typeof handler === 'string') {
                    handler = this[handler];
                }
                if (handler instanceof Function) {
                    handler.apply(this, data);
                }
            }
        }
        return this;
    };

    return target;
};

Этот компонент при инициализации создает в указанном объекте 3 метода:

  • attachObserver — добавляет колбек для определенного события
  • detachObserver — удаляет один или все колбеки для определенного события
  • notify — инициализирует событие, при этом вызываются все зарегистрированные колбеки с указанными аргументами

Все указанные методы поддерживают цепочный вызов.

var FieldObject = function (root) {
    var me = Observable(this);

    $(root).append('<div class="field"><input type="text" value="" /></div>');
    $('div.field input', root).change(function () {
        me.notify('valueDidChange', this.value);
    });

    return me;
};

FieldObject($('body'))
    .attachObserver('valueDidChange', function (data) {
        alert(data);
    });

В этом примере я создал простой программный компонент FieldObject, которой добавляет на страницу текстовое поле, а затем подписался на событие valueDidChange, которое случается при обновлении этого текстового поля. Весь фокус в том, что я могу не знать, как устроен этот объект, и откуда получить значение его текстового поля. Все что мне нужно – это отреагировать на изменение поля.