Шаблоны кода, чтобы сделать ваши программы более надежными, эффективными и облегчить вашу жизнь
Я занимаюсь разработкой EDR-решений 7 лет.
Это означает, что мне нужно написать долговечное системное программное обеспечение, которое будет одновременно отказоустойчивым и эффективным.
Я активно использую Go для этой работы, и я хотел бы поделиться некоторыми из наиболее важных шаблонов кода, на которые вы можете рассчитывать, чтобы сделать свою программу более надежной и эффективной.
Использовать карты как набор
Нам часто нужно проверить наличие чего-либо. Например, мы могли бы захотеть проверить, был ли ранее посещен путь к файлу / URL / ID. В этих случаях мы можем использовать map[string]struct{}
. Например:
Использование пустой структуры struct{}
означает, что мы не хотим, чтобы часть значения карты занимала какое-либо пространство. Иногда люди используют map[string]bool
, но тесты показали, что map[string]struct{}
работает лучше как по памяти, так и по времени.
Также стоит отметить, что обычно считается, что операции с картой имеют O(1)
временную сложность (StackOverflow), но время выполнения go не дает такой гарантии.
Использование chan struct {} для синхронизации горутин
Каналы могут передавать данные, но это не обязательно. Иногда они нужны нам просто для синхронизации.
В следующем случае канал несет тип данных struct{}
, который представляет собой пустую структуру, не занимающую места. Это тот же трюк, что и в предыдущем примере карты:
Использовать близко к трансляции
Продолжая предыдущий пример, если мы запускаем несколько go hello(quit)
, то вместо отправки нескольких struct{}{}
на quit
мы можем просто закрыть канал quit
для трансляции сигнала:
Обратите внимание, что закрытие канала для трансляции сигнала работает с любым количеством горутин, поэтому close(quit)
также применяется в предыдущем примере.
Использование нулевого канала для блокировки выбранного случая
Иногда нам нужно отключить определенные случаи в операторе select, как в следующей функции, которая читает из источника событий и отправляет события в канал отправки. (Этот вид функции обычно включает в себя обработку необработанных данных для формирования объектов событий, но давайте перейдем к делу).
Что мы хотим улучшить:
- отключите
case s.dispatchC
, когдаlen(pending) == 0
, чтобы код не паниковал - отключите
case s.eventSource
приlen(pending) >= maxPending
, чтобы не выделять слишком много памяти
Хитрость здесь в том, чтобы использовать дополнительную переменную для включения / выключения исходного канала, а затем использовать эту переменную в случае выбора.
Предупреждение: будьте осторожны, чтобы не отключать все случаи одновременно, иначе цикл for-select перестанет работать.
Неблокирующее чтение из канала
Иногда мы хотим предлагать услуги по принципу «максимальные усилия». То есть мы намеренно хотим, чтобы канал работал с потерями.
Это имеет смысл, когда, например, у нас есть чрезмерное количество событий для отправки получателям, и некоторые из них могут не реагировать. Мы можем игнорировать этих неотзывчивых получателей, чтобы:
- Отправка другим получателям вовремя
- Избегайте выделения слишком большого количества памяти для ожидающих событий
Анонимный Struct
Иногда нам просто нужно, чтобы контейнер содержал группу связанных значений, и такая группировка больше нигде не появится. В этих случаях нас не волнует его тип. В Python мы могли бы создать словарь или кортеж для таких ситуаций. В Go вы можете создать анонимную структуру для такого рода ситуаций. Я проиллюстрирую это двумя примерами.
Случай 1: Конфигурация
Итак, вы хотите сгруппировать свои значения конфигурации в переменную. Но создание шрифта для него кажется излишним.
Итак, вместо этого:
Ты можешь сделать это:
Обратите внимание, что struct {...}
- это тип переменной Config
- теперь вы можете получить доступ к своим значениям конфигурации через Config.Timeout
.
Случай 2: контрольные примеры
Допустим, вы хотите протестировать свою причудливую Add()
функцию вместо того, чтобы писать множество операторов if-else вроде этого:
Вы можете разделить свои тестовые примеры и логику тестирования следующим образом:
Это окупается, когда у вас много тестовых примеров или когда вам иногда нужно изменить логику тестирования.
Определенно есть другие сценарии, в которых вам могут пригодиться анонимные структуры. Например, если вы хотите проанализировать следующий JSON, вы можете определить анонимную структуру с вложенными анонимными структурами, чтобы вы могли анализировать ее с помощью библиотеки encoding/json
.
Варианты упаковки с функциями
Иногда у нас есть сложная структура с множеством необязательных полей, и вам очень не хватает возможности использовать необязательные аргументы в Python:
Мой любимый способ добиться этого в Go - обернуть эти параметры (порт, прокси) с помощью функций. То есть мы можем создавать функции для применения значений наших опций, которые хранятся в закрытии функции.
Используя приведенный выше пример, у нас есть 2 необязательных поля (порт, прокси), которые пользователи могут указать при создании экземпляра Client
:
Такой способ упаковки упрощает использование и, что более важно, удобство чтения:
Заключение
Итак, мы говорили о
- Использование
map[string]struct{}
как установлено - Использование
chan struct{}
для эффективной синхронизации горутин и использованиеclose()
для широковещательной передачи сигналов произвольному количеству горутин. - Установка переменной канала в
nil
, чтобы отключить отдельные случаи - Создание каналов с потерями по шаблону
select-default
- Использование анонимных структур для группировки значений конфигурации и тестовых случаев
- Параметры упаковки как функции
Если вы опытный программист на Go, то, вероятно, уже видели эти шаблоны кода раньше. Однако, когда я только начал программировать на Go, для меня это было совсем не очевидно.
Go - очень мощный язык, конструкция которого сильно отличается от большинства знакомых нам языков (например, C / C ++, Python, PHP, Java и т. Д.). Поэтому очень важно правильно использовать его красивый синтаксис, иначе вы можете столкнуться с очень неприятными ошибками, которые либо трудно запустить, либо вы можете не знать, откуда они взялись.
Я попытался изобразить суть Go с помощью приведенных выше шаблонов кода, но они далеки от завершения. Чтобы узнать больше, я бы порекомендовал посмотреть отличные выступления от Google.