Когда крючки - правильный выбор дизайна?

10

Я работал над большим приложением Rails, где использование обратных вызовов ActiveRecord было безудержным и мучительным. Сохранение записи часто имело неожиданные побочные эффекты, и это было непросто.

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

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

Тема, которая бросается в глаза, - это предсказуемость. Злоупотребление хуками, кажется, приводит к жутким действиям на расстоянии и удивительному поведению, тогда как правильное использование может привести к структуре, которая предсказуема без жесткой связи.

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

иван
источник

Ответы:

5

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

Вы можете сделать это с помощью анонимной трансляции (например, событий или анонимных обратных вызовов) или с помощью типизированных абстракций (например, интерфейсов или родительских классов).

Вы должны использовать анонимную трансляцию, когда:

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

Вы должны использовать типизированную абстракцию, когда:

  • Вызов крюка обязателен.
  • Вызывающий объект должен идентифицировать получателя ловушки.
  • Порядок исполнения получателей актуален для вашего объекта.

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

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

Сезар Эрнандес
источник
7

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

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

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

Как правило, используйте перехватчики, потому что они являются характеристиками производительности и заставят ваш код работать более эффективно, чем альтернативный (который называется «опросом», когда вы ожидаете в занятом цикле проверки событий). Однако также убедитесь, что вы используете соответствующую документацию и обновляете ее. Крючки используются практически на каждом языке, с которым вы столкнетесь, и важно знать, почему они существуют.

phyrfox
источник
1

Ранее я работал над программой, которая, по моему мнению, хорошо использовала хуки. В моей книге истинный перехват - это вызов (публикация) списка обратных вызовов (подписчиков). Это мощно, злоупотреблять легко. Лучший вопрос, который следует задать для варианта использования: хочу ли я включить или отключить поведение во время выполнения? Например, вы можете разрешить своим пользователям предоставлять сценарии, которые вы будете запускать при определенных событиях, или создать общую программу из плагинов меньшего размера. Тогда вызовы ловушки уместны. Как правило, у вас есть немного других вариантов.

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

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

Артур Гавличек
источник