Есть ли предпочтительный способ разработки API сигналов или событий в Go?

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

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

Во многих языках или фреймворках, с которыми я играл, существуют стандартные способы создания этих API-интерфейсов, чтобы они вели себя так, как ожидают пользователи: например. функции g_signal_* для glib-приложений, событий и addEventListener() для приложений DOM для JavaScript или многоадресных делегатов для .NET.

Есть ли что-то похожее для Go? Если нет, есть ли другой способ структурирования этого типа API, который более идиоматичен в Go?

Ответы

Ответ 1

Я бы сказал, что горутин, получающий от канала, в определенной степени является аналогом наблюдателя. Идиоматический способ разоблачения событий в Go будет таким образом ИМХО, чтобы возвращать каналы из пакета (функции). Другое наблюдение заключается в том, что обратные вызовы слишком часто не используются в программах Go. Одной из причин является также наличие мощного select statement.

Как последнее замечание: некоторые люди (я тоже) рассматриваю шаблоны GoF как антипаттеры Go.

Ответ 2

Go дает вам множество инструментов для проектирования сигнала api.

Сначала вам нужно решить несколько вещей:

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

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

Ответ 3

Мне понадобилось "тип шаблона наблюдателя" в нескольких проектах. Вот пример повторного использования из недавнего проекта.

Он получил соответствующий тест, который показывает, как его использовать.

Основная теория заключается в том, что излучатель события вызывает Submit с некоторой частью данных всякий раз, когда происходит что-то интересное. Любой клиент, который хочет знать об этом событии, будет Register каналом, с которого он считывает данные события. Этот зарегистрированный канал можно использовать в цикле select, или вы можете прочитать его напрямую (или буферизировать и опросить его).

Когда вы закончите, вы Unregister.

Он не идеален для всех случаев (например, я могу захотеть тип отмены регистрации для замедленных наблюдателей), но он работает там, где я его использую.

Ответ 4

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