Анализ структурных тегов
Отрывок из Эффективных рецептов го Мики Тебека
Задача
Вы создаете ORM (объектно-реляционный преобразователь) для собственной базы данных вашей компании. ORM упрощает работу с базой данных; вы сохраняете и извлекаете структуры Go напрямую, вместо использования []any
.
Ваша первая задача — проанализировать пользовательскую структуру и вернуть соответствие между именами полей и столбцами базы данных. Например, для следующей структуры:
// Log is a log structure type Log struct { Time time.Time `db:"ts"` Level int `db:"level"` Text string `db:"message"` }
Вы должны вернуть отображение:
- Время → тс
- Уровень → уровень
- Текст → сообщение
Решение
Вы начинаете с определения сигнатуры функции синтаксического анализа:
func parseStructTags(s any) (map[string]string, error) {
Сначала вы используете пакет отражения, чтобы получить тип параметра:
typ := reflect.TypeOf(s) if typ.Kind() != reflect.Struct { return nil, fmt.Errorf("%s is not a struct", typ) }
Затем вы перебираете поля структуры и извлекаете ключ db
из тега поля:
m := make(map[string]string) for i := 0; i < typ.NumField(); i++ { fld := typ.Field(i) if dbName := fld.Tag.Get("db"); dbName != "" { m[fld.Name] = dbName } } return m, nil
Обсуждение
Поскольку parseStructTags
должен принимать любое значение, тип его параметра — any
. В большинстве случаев использование any
является запашком кода, и вам следует использовать конкретные типы или интерфейс.
Однако в этом случае вы не можете использовать конкретный тип или интерфейс, поэтому вам остается использовать any
.
📝 Примечание. Термин запах кода был придуман Кентом Беком и означает поверхностный признак, который обычно соответствует более глубокой проблеме в системе.
Теги структур используются многими пакетами сериализации; их используют встроенные encoding/json
, encoding/xml
и многие другие внешние пакеты, такие как yaml
.
Структурные теги позволяют добавлять дополнительную информацию о поле. Они имеют известный формат: key:”value” key:”value” …
Тип Field
в пакете reflect
имеет методы для извлечения определенного ключа.
В некоторых случаях значение может быть больше, чем просто имя. Обычно это список значений, разделенных ,
или name=value
. Вот пример структуры, созданной инструментом Google protoc
:
Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"`
Тег структуры имеет два ключа: protobuf
и json
, и оба имеют комплексные значения.
Если вы решите, что вам нужны сложные значения (например, protobuf
в нашем примере), вам необходимо разработать формат, используемый в этих тегах, поскольку вы по сути реализуете свой собственный формат сериализации.
Лучше копировать то, что делают encoding/json
или protobuf
, а не изобретать что-то свое.
Мы надеемся, что вам понравился этот отрывок из Эффективных рецептов го Мики Тебеки. Книга сейчас находится в бета-версии от The Pragmatic Bookshelf:
Нам бы хотелось услышать ваше мнение, поэтому, если вы получите бета-версию, поделитесь комментариями и предложениями на странице книги на DevTalk.