Padrões de design para coordenar ouvintes de eventos de mudança

5

Eu tenho trabalhado com o padrão Observer em JavaScript usando várias bibliotecas populares por vários anos (YUI e jQuery). Muitas vezes, é necessário observar um conjunto de alterações no valor da propriedade (por exemplo, responda somente quando dois ou mais valores específicos são alterados). Existe uma maneira elegante de 'inscrever' o manipulador para que ele seja chamado apenas uma vez? Há algo que estou perdendo ou fazendo de errado no meu design?

    
por Kreegr 21.04.2012 / 04:34
fonte

4 respostas

1

Sim. Para isso, voltei ao livro original "Gang of Four" em Design Patterns e fiz algumas pesquisas on-line.

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.

Aqui está um exemplo de mediador JavaScript . Pule para o fundo para uso. Outros exemplos podem ou não ser mais relevantes para você. Mas isso exemplifica a noção.

"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

Aqui está um código:

//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.}

// Um mediador adiciona e remove componentes e transmite eventos.

// Um mediador faz um loop através de objetos adicionados e estado de referências para determinar se a ação deve ser tomada.

    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(' '));
            }
        }
    }

Sua implementação do comportamento "aplicar" provavelmente é diferente e pode ser simplificada.

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

Eu não pensaria nisso como "limitar os eventos". Provavelmente, não há ganho real de desempenho fazendo isso de qualquer maneira. A perspectiva acima, é introduzir um mecanismo de decisão que diz: "Nós tivemos 2 ou mais objetos disparando eventos, vamos fazer essa tarefa especial".

O mediador encapsula o mecanismo de decisão, o objeto de estado encapsula o acúmulo de informações em tempo de execução. Tenha em mente que, em algum momento, o estado pode precisar ser redefinido. Este comportamento de limpeza seria sensato para ser colocado dentro do mediador.

Pode muito bem acontecer de você ter o seu Observer ouvindo os eventos publicados e, em seguida, chamar o Mediador para revisar o Estado, o que determina a interação, se for relevante, e então transmite o evento com instrução para executar o evento. comportamento especial.

EDIT: Confira também as Views Evented. Este é um artigo excepcional sobre o assunto.

    
por 21.06.2012 / 17:37
fonte
1

A estrutura Extensões reativas foi projetada para resolver esse problema para os manipuladores de eventos .NET. Você pode estar interessado em saber como o RX permite responder a sequências de eventos, conforme descrito na seção "Eventos de sequenciamento" de esta postagem no blog . Basicamente, se você está interessado em responder apenas quando o usuário digita "ABC" (nessa ordem), você pode dizer:

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 é a implementação do javascript.

    
por 21.06.2012 / 21:08
fonte
1

Uma técnica (usando sistemas baseados em eventos com um mediador):

Eu aproveito bastante o Padrão do Mediador - uma maneira de controlar o fluxo de eventos é instanciar diferentes objetos Mediator por módulo e passar apenas um Mediador específico para cada módulo (como um, ou em uma caixa de proteção) - isso permitirá que cada módulo só se comunique através desse meio específico, somente quando você diz que deve - e, com este método, quando você transmitir mensagens, você não estará transmitindo em todo o sistema / mídia que o ApplicationDirector está escutando. Uma implementação simplificada pode ser algo assim:

// 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 */ });
    // ...
});

Agora, você pode usar um Mediador para gerenciar quando exatamente algumas alterações (por exemplo, quantidade) afetam o restante do sistema. Você ainda tem mais controle sobre quando um determinado módulo deve notificar o sistema da (s) alteração (ões). Além disso, se você estiver desenvolvendo um aplicativo de grande escala - usar um mediador que implemente um EventHub é uma boa abordagem.

Há muitas coisas estranhas sobre este exemplo , mas espero que você ache isso útil para o que demonstra.

    
por 10.12.2014 / 02:40
fonte
0

Eu não sei, se essa idéia já existir e se tiver um nome - se for assim, seria legal se alguém nos dissesse: -)

Você pode adicionar um objeto que

  • observa seus dois ou três valores,
  • é observado pelo seu objeto de objetivo e
  • envia apenas uma mensagem, se sua condição for atingida.

Isso soa para mim como um "proxy observador condicional".

    
por 21.04.2012 / 10:39
fonte