Что должны использовать плагины: хуки, события или что-то еще?

24

Рассмотрим приложение, которое позволяет плагинам реагировать на ход программы.

Я знаю 2 способа добиться этого: хуки и события

1. Крючки

Используйте вызовы для очистки функций внутри основного потока программы. Эти функции могут быть переопределены плагинами.

Например, Drupal CMS реализует хуки, которые доступны для модулей и тем. Вот пример того, как ловушка реализована в функции 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;
    // ...
}

Модуль может реализовать modulename_file_copy($file, $source)функцию, которая будет вызываться функцией module_invoke_allin file_copy. После завершения этой функции file_copyвыполнение возобновится.

2. События

Получите от приложения события отправки, которые могут прослушиваться плагинами. После получения события, на которое он подписан, плагин будет перехватывать поток программы и выполнять необходимые операции.

Например, плагин галереи jQuery Fotorama реализует несколько событий . Например, вот часть его showметода, который запускает fotorama:showсобытие.

  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.]
  };

Скрипт может прослушивать это событие и что-то делать при его запуске:

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

ВОПРОС

  1. Существуют ли другие основные способы реализации такого поведения плагина?

  2. Если нет, то когда следует использовать хуки и когда следует использовать события? Учитывая конечную цель - сделать код более понятным и читаемым как с точки зрения приложения, так и с точки зрения разработчика плагина?

sbichenko
источник

Ответы:

17

Основным отличием между крючком и событием является слабая связь по сравнению с жесткой.

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

События более тесно связаны с приложением. События могут определять параметры, которые присоединяются к событию, и если вы изменяете эти параметры, вы нарушаете API с помощью существующих плагинов.

Они оба достигают одинаковых результатов. Это зависит только от того, как вы хотите подключить плагин к приложению.

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

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

Вы просили альтернативные подходы.

Команды:

Вместо плагинов, реагирующих на запускаемые события. Плагины передают объекты команд в приложение. Каждый объект команды реализует интерфейс, используемый командами. Когда приложение должно выполнить функцию, оно запускает все команды для этой функции. Это очень похоже на события, за исключением того, что оно реализовано в виде объектов вместо функций обратного вызова.

Макросы:

Вместо того, чтобы плагины реагировали на события. Плагины активно вызывают события. Макрос - это небольшой язык высокого уровня, который запускается над приложением и говорит ему, что делать.

Слушатели изменения состояния:

События запускаются приложением с продуманным замыслом разработчика. Разработчик должен сознательно написать код, который создает событие. Вместо этого альтернативный подход состоит в том, чтобы заставить объекты автоматически транслироваться, когда их внутреннее состояние изменилось. Либо изменение свойства, либо другие показатели. Затем плагины могут прослушивать эти конкретные изменения состояния и реагировать соответствующим образом. Преимущество такого подхода заключается в том, что программисту не нужно запоминать события трансляции. Например, может существовать объект Document, и программист устанавливает флаг, чтобы отметить, что документ должен быть сохранен. Это изменение состояния транслируется на прослушивающие плагины, и может существовать плагин, который изменяет заголовок документа и включает звездочку.

Reactgular
источник
2
+1 для альтернатив, -1 для определения и сцепного аргумента (который делает существует , но сцепление является следствием конструктивных вариантов, в зависимости от того названия вы даете вашу систему плагин)
5
Я думаю, что вы также делаете предположения о том, как событие перемещается от генератора к наблюдателю / слушателю. На самом деле все наоборот, крючки тесно связаны, а события - нет.
Ахмед Масуд
3

Определенно события, это позволяет необходимую абстракцию уже на архитектурном уровне.

Не ожидайте, что кто-либо, пишущий плагин, на самом деле делает это, как задокументировано или каким-либо образом правильно. Я поддерживаю хорошо документированный API с миллионами пользователей, и я могу сказать вам по очень болезненному опыту, что практически никто никогда не читает документацию и почти никто не использует API правильно.

Возьмем следующий пример с хуками: у вас есть система, в которой запущено 20 плагинов. Один из этих плагинов вызывает file_copyметод так, как он задокументирован, и ожидает результат в том виде, в котором он задокументирован. Но какой-то другой плагин перехватил эту функцию, и поэтому одна из следующих проблем вызывает сбой или неисправность:

  • Функция ловушки просто вылетает. Все остальные плагины теперь испорчены, потому что они больше не могут file_copy или функция работает не так, как ожидается.
  • Входные данные верны на основании документации, но другой плагин не ожидает этого и выдает странные результаты или сбои.
  • Вызов работает нормально, но результат больше не соответствует ожидаемому согласно документации, поэтому плагин выходит из строя или дает сбой.

Если вы делаете то же, что и выше, с событиями с теми же проблемами внутри этих плагинов, происходит следующее:

  • Функция события плагина X вылетает, но все остальные работают нормально. Но поскольку эти плагины не связаны между собой, вы можете просто отключить этот аварийный плагин, в то время как другие продолжают работать нормально.
  • Странный ввод может быть правильно обработан вашей функцией, и вы можете правильно проверить все возможные вещи для каждого плагина в отдельности. Разработчик плагина теперь имеет стабильный и надежный способ реального тестирования своего плагина, что позволяет ему быть уверенным, что, если он работает для него, он будет работать для всех. Если один плагин обеспечивает неправильный ввод, он может быть изолирован для этого одного плагина.
  • В равной степени результат может быть проверен и правильно определен при любых обстоятельствах, поэтому разработчик плагина имеет стабильный и надежный ответ от той функции, которую он / она может протестировать.
TwoThe
источник
1

Наследование может быть вариантом.

Помимо хуков, наследование не требует дополнительных определений методов, и при вызове пустого метода не происходит потери производительности в случае, если ничего не подключено.

Помимо событий, наследование также не требует дополнительного кода для вызова события.

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

Томас Веллер
источник
-1 Потому что вы используете Inheritance, а затем изменяете код создания экземпляров, чтобы использовать вашу спецификацию и злоупотреблять наследованием, поскольку новое поведение имеет другое назначение в качестве основного приложения ...
SparK
0

Определенно события. Это позволяет вашей архитектуре быть более масштабируемой.

Представьте, что произойдет, если вам нужно, например, разместить свой плагин на отдельном компьютере. Использование событий - вам нужно всего лишь изменить небольшую часть кода, чтобы ваши события основывались на сети.

VPOL
источник