O que os plugins devem usar: ganchos, eventos ou algo mais?


24

Considere um aplicativo que permita que os plug-ins reajam ao fluxo do programa.

Conheço duas maneiras de conseguir isso: ganchos e eventos

1. Ganchos

Use chamadas para funções vazias dentro do fluxo principal do programa. Essas funções podem ser substituídas por plugins.

Por exemplo, o Drupal CMS implementa ganchos disponíveis para módulos e temas. Aqui está um exemplo de como o hook é implementado em uma função file_copy .

function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
    // ... [File copying routine]

    // Inform modules that the file has been copied.
    module_invoke_all('file_copy', $file, $source);

    return $file;
    // ...
}

Um módulo pode implementar uma modulename_file_copy($file, $source)função que será chamada pelo module_invoke_allin file_copy. Após o término desta função, a file_copyexecução será retomada.

2. Eventos

Tenha os eventos de despacho do aplicativo, que podem ser ouvidos pelos plug-ins. Depois de receber um evento no qual foi inscrito, um plug-in interceptará o fluxo do programa e executará operações necessárias.

Por exemplo, um plug-in da galeria jQuery Fotorama implementa vários eventos . Como exemplo, aqui está uma parte de seu showmétodo que dispara o fotorama:showevento.

  that.show = function (options) {
    // ... [show the new frame]

    // [fire the event]
    options.reset || triggerEvent('show', {
      user: options.user,
      time: time
    });

    // ... [do lots of other stuff with navigation bars, etc.]
  };

Um script pode ouvir esse evento e fazer alguma coisa quando é acionado:

$('.fotorama').on(
  'fotorama:show',
  function (e, fotorama, extra) {
    console.log(e.type + (extra.user ? ' after user’s touch' : ''));
    console.log('transition duration: ' + extra.time);
  }
);

QUESTÃO

  1. Existem outras maneiras comuns de implementar esse comportamento de plug-in?

  2. Caso contrário, quando se deve usar ganchos e quando se deve usar eventos? Considerando que o objetivo final é tornar o código mais fácil de ler e legível, tanto da perspectiva do desenvolvedor do aplicativo quanto do plugin?

Respostas:


17

A principal diferença entre um gancho e um evento é o acoplamento solto versus o acoplamento apertado.

Um gancho é uma maneira genérica de transmitir que algo aconteceu. Você pode adicionar novos ganchos sem precisar recompilar plug-ins, e todos os ganchos seguem um padrão de design genérico. Uma vez definida a API do gancho, ela não muda, portanto, o acoplamento entre o aplicativo e o plug-in provavelmente não será interrompido.

Os eventos são mais fortemente acoplados ao aplicativo. Eventos podem definir parâmetros anexados ao evento e, se você alterar esses parâmetros, interromperá a API com plug-ins existentes.

Ambos alcançam os mesmos resultados. Depende apenas de como você deseja associar o plug-in ao aplicativo.

Hooks pode oferecer um acoplamento mais dinâmico que provavelmente não será interrompido quando novas versões do seu aplicativo forem lançadas, mas a desvantagem é que você não recebe nenhum aviso de tempo de compilação de que os plug-ins não são mais compatíveis.

Os eventos oferecem a capacidade de obter erros de tempo de compilação que o plug-in precisa ser modificado, porque algumas das assinaturas de eventos foram alteradas.

Você pediu abordagens alternativas.

Comandos:

Em vez de plugins responderem a eventos acionados. Os plug-ins enviam objetos de comando ao aplicativo. Cada objeto de comando implementa uma interface usada por comandos. Quando o aplicativo precisa executar um recurso, ele executa todos os comandos para esse recurso. Isso é muito parecido com eventos, exceto que ele é implementado como objetos em vez de funções de retorno de chamada.

Macros:

Em vez de plugins responderem quando as coisas acontecem. Os plug-ins proativamente fazem as coisas acontecerem. Uma macro é uma pequena linguagem de alto nível executada acima do aplicativo, informando o que fazer.

Ouvintes de mudança de estado:

Os eventos são acionados pelo aplicativo com premeditação do desenvolvedor. O desenvolvedor precisa escrever conscientemente o código que emite o evento. Em vez disso, uma abordagem alternativa é fazer com que os objetos sejam transmitidos automaticamente quando seu estado interno for alterado. A alteração de uma propriedade ou outros indicadores. Os plug-ins podem ouvir essas alterações de estado específicas e reagir de acordo. A vantagem dessa abordagem é que o programador não precisa se lembrar de transmitir eventos. Por exemplo, pode haver um objeto Document e o programador define um sinalizador para marcar que o documento precisa ser salvo. Essa alteração de estado é transmitida para plug-ins de escuta e pode haver um plug-in que altera o título do documento para incluir um asterisco.


2
+1 para as alternativas, -1 para as definições e o argumento de acoplamento (que faz existir, mas de acoplamento é uma consequência de escolhas de design, o que nome você dá ao seu sistema de plugin)

5
Eu acho que você também está fazendo suposições sobre como um evento viaja do gerador para o observador / ouvinte. Por uma questão de fato, é o contrário, os ganchos estão fortemente acoplados, enquanto os eventos não.
Ahmed Masud 12/05

3

Definitivamente eventos, ele permite a abstração necessária já no nível arquitetural.

Não espere que alguém que esteja escrevendo um plugin realmente o faça como documentado ou de alguma forma correto. Eu tenho mantido uma API bem documentada com milhões de usuários e posso dizer a partir de uma experiência muito dolorosa que basicamente ninguém nunca lê a documentação e quase ninguém usa a API corretamente.

Veja o seguinte exemplo com ganchos: Você tem um sistema em que 20 plugins estão em execução. Um desses plugins chama o file_copymétodo da maneira como está documentado e espera um resultado conforme documentado. Mas algum outro plugin conectou essa função e, portanto, um dos seguintes problemas causa uma falha ou mau funcionamento:

  • A função de gancho simplesmente trava. Todos os outros plugins estão ferrados agora, porque não podem mais file_copy ou a função funciona de maneira diferente do esperado.
  • A entrada está correta com base na documentação, mas o outro plug-in não espera e oferece resultados estranhos ou falhas.
  • A chamada é executada corretamente, mas o resultado não é mais o esperado, de acordo com a documentação, portanto o plug-in falha ou trava.

Se você fizer o mesmo que acima com eventos com os mesmos problemas dentro desses plugins, acontece o seguinte:

  • A função de evento do plug-in X falha, mas todos os outros funcionam bem. Mas como esses plug-ins não estão relacionados, você pode simplesmente desativar o plug-in com falha enquanto os outros continuam funcionando bem.
  • Entrada estranha pode ser tratada adequadamente por sua função e você pode verificar corretamente todas as coisas possíveis para cada plug-in individualmente. O desenvolvedor de plug-ins agora tem uma maneira estável e confiável de testar seu plug-in, o que lhe permite ter certeza de que, se funcionar para ele, funcionará para todos. Se um plug-in fornecer entrada incorreta, ele poderá ser isolado para esse plug-in.
  • Da mesma forma, o resultado pode ser verificado e definido adequadamente em todas as circunstâncias, para que o desenvolvedor do plug-in tenha uma resposta estável e confiável dessa função que ele / ela pode testar.

1

A herança pode ser uma opção.

Além de ganchos, a herança não precisa de definições de método extras, e não há perda de desempenho para chamar o método vazio, caso não haja nada conectado.

Além de eventos, a herança também não precisa de código extra para invocação de eventos.

No entanto, a herança funciona melhor se houver apenas um plugin modificando um tipo de comportamento. Se você precisar de muitos plugins, o segundo precisará derivar do primeiro, etc., o que não é apropriado.


-1 Porque você usar a herança e, em seguida, alterar o código instantiating usar sua especificação e herança uso indevido como o novo comportamento tem um propósito diferente como a aplicação principal ...
faísca

0

Definitivamente eventos. Permite que sua arquitetura seja mais escalável.

Imagine o que acontecerá se você precisar colocar seu plug-in em uma máquina separada, por exemplo. Usando eventos - você precisará modificar apenas uma pequena quantidade de código para tornar seus eventos baseados em rede.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.