Разделение кода клиент / сервер
Я разрабатываю клиент-серверное приложение в golang, и существуют определенные логические объекты, которые существуют как на клиенте, так и на сервере (список ограничен)
Я хотел бы убедиться, что определенный код для этой сущности включен ТОЛЬКО в серверную часть, а НЕ в клиент (наоборот, это хорошо, но не так важно).
Наивной мыслью было бы полагаться на устранение мертвого кода, но из моего краткого исследования это не надежный способ справиться с этой задачей... go build
просто не удалит мертвый код из-за того, что он мог быть использован с помощью отражения (никого не волнует, что это не так, и нет возможности настроить это)
Более надежный подход, по-видимому, заключается в разделении кода на разные пакеты и соответствующем импорте, это кажется надежным, но чрезмерно усложняет код, заставляя вас физически разделять определенные объекты между различными пакетами и постоянно помнить об этом...
И, наконец, есть теги сборки, позволяющие создать несколько файлов в одном пакете условно для клиента и сервера.
Мотивация использования тегов компоновки заключается в том, что я хочу сохранить код максимально чистым, не вводя никаких синтетических объектов
Вариант использования: есть определенные подпрограммы шифрования, клиент работает с открытым ключом, сервер работает с закрытым... Код логически принадлежит одной и той же сущности
Какой вариант вы бы выбрали и почему?
1 ответ
Это "устранение мертвого кода" уже сделано - частично - инструментом go. Инструмент go не включает в себя все из импортированных пакетов, только то, что необходимо (или точнее: он исключает вещи, которые могут оказаться недоступными).
Например это приложение
package main; import _ "fmt"; func main() {}
приводит к уменьшению размера исполняемого двоичного файла почти на 300 КБ (в Windows amd64) по сравнению со следующим:
package main; import "fmt"; func main() {fmt.Println()}
Исключаемые вещи включают функции, типы и даже не экспортируемые и экспортируемые переменные. Это возможно, потому что даже с отражением вы не можете вызывать функцию или "создавать" типы или ссылаться на переменные пакета, просто имея их имена как string
значение. Так что, может быть, тебе не стоит так об этом беспокоиться.
Редактировать: С выпуском Go 1.7, это даже лучше: прочитайте сообщение в блоге: Smaller Go 1.7 binaries
Таким образом, если вы хорошо проектируете свои типы и функции и не создаете "гигантские" реестры, в которых вы перечисляете функции и типы (которые явно генерируют ссылки на них и, таким образом, делают их неисключаемыми), скомпилированные двоичные файлы будут содержать только то, что фактически используется из импортные пакеты.
Я бы не советовал использовать теги сборки для такого рода проблем. Используя их, вы несете дополнительную ответственность за сохранение зависимостей пакетов / файлов самостоятельно, что в противном случае выполняется инструментом go.
Вы не должны разрабатывать и разделять код на пакеты, чтобы сделать ваши выходные исполняемые файлы меньше. Вы должны разработать и разделить код на пакеты на основе логики.
Я хотел бы разделить содержимое на пакеты, когда это действительно необходимо, и импортировать соответствующим образом. Потому что это действительно то, что вам нужно: какой-то код предназначен только для клиента, другой - только для сервера. Возможно, вам придется подумать немного больше на этапе проектирования и кодирования, но, по крайней мере, вы увидите результат (что на самом деле принадлежит / компилируется в клиент и на сервер).