Во-первых, позвольте мне признать, что мне очень нравится Голанг. Я считаю, что это довольно элегантный, легкий для чтения и очень мощный язык. Но также я люблю C #.
Хотя я знаю, что у каждого языка есть свои лучшие практики и свой собственный способ создания хорошего программного обеспечения, в C # есть кое-что, чего мне определенно не хватает в Golang. Одна из таких вещей - LINQ (Language-Integrated Query).
LINQ позволяет нам манипулировать коллекциями на C #, изменять, фильтровать, группировать их с помощью анонимных (лямбда) функций. Давайте посмотрим на небольшом примере, как это выглядит:
Эта проблема
Одним из самых больших неудобств, с которыми я столкнулся в Go, было то, что в языке нет стандартного способа фильтрации фрагмента или поиска элемента фрагмента по одному из его полей. Я пытался найти способ сделать это, но все, что я нашел, было что-то вроде «Ну, вы должны написать для этого цикл».
Однажды я обнаружил, что в моей программе было несколько циклов, и они были почти одинаковыми, но работали с разными типами. Было что-то в этом роде.
Это заставило меня подумать, что это должен быть способ избежать копирования кода. Один из подходов - сделать общий метод получения interface{}
.
И в этой реализации я вижу по крайней мере пару проблем.
Во-первых, существует множество утверждений типа (например, x.(B).FieldString…
и т. Д.). Это выглядит не очень красиво и заставляет нас создавать много кода, чтобы проверить, можно ли сделать утверждение.
Кроме того, это заставляет нас использовать пакет reflect
. Многие приложения Go созданы, чтобы быть быстрыми, я имею в виду, очень быстрыми. Кое-где можно было слышать, что reflect
- не лучший помощник, если вы ищете хорошую производительность.
И еще один недостаток - это базовый тип, возвращаемый нашим filter
методом. Давайте проверим его с fmt.Printf(“%T”, filteredA)
, и он вернет нам: []interface {}
. Если мы хотим использовать этот объект, это было бы не круто. Нам нужно будет перебрать его, чтобы преобразовать каждый тип элемента в A
.
Итак, все эти проблемы заставили меня подумать, что мы не должны создавать псевдо-общий метод для всех типов. Но написание отдельного цикла для каждого типа (как в первом примере кода) тоже выглядит не очень хорошо.
Я предположил, что создание генератора кода для создания некоторых общих методов, которые не будут использовать пакет reflect
внутри этих методов, может помочь. Вот как мы можем избежать постоянного копирования и вставки кода, а также иметь красивые и понятные методы, которые принимают и возвращают определенные типы вместо того, чтобы раздражать interface{}
.
Представляем госли
Эти мысли привели меня к созданию библиотеки, которую я назвал gosli
. На самом деле я не планировал придавать этому большого значения, я просто написал код, который, как мне кажется, может быть полезен кому-то другому.
TL; DR: проверьте исходный код и примеры использования здесь https://github.com/doctornick42/gosli
Хорошо, давайте начнем. Чтобы его получить, нам нужно запустить эту команду:
go get github.com/doctornick42/gosli
Создадим новый проект с такой структурой:
testtypes.go
будет содержать наши типы из предыдущих примеров:
После этого мы должны запустить это для Linux
$GOPATH/bin/gosli types/testtypes.go A
или это для Windows:
%GOPATH%\bin\gosli.exe types\testtypes.go A
Как видите, наша папка types
была расширена за счет добавления 3 новых файлов:
Единственный из них, который нам нужно изменить вручную, - это a_equal.go
, который необходим gosli, чтобы понять, как он может проверить, равны ли два экземпляра A
. С нуля код этого файла выглядит так:
Добавим после // `equal` method has to be implemented manually
комментария новую строку:
return r.FieldInt == another.FieldInt
Вот и все. Теперь мы можем написать код для проверки результата, давайте просто воспользуемся кодом, который мы использовали в предыдущем примере, и поместим его в наш main.go
файл:
И если мы запустим программу, мы увидим такой результат: [{“FieldInt”:1},{“FieldInt”:3}]
, что правильно.
Полный код этого примера здесь.
Особенности госли
У библиотеки есть 2 основных подхода:
- чтобы предоставить методы для фрагментов настраиваемых типов. В этой части используется генерация кода, она была кратко описана в предыдущем абзаце. Документацию, показывающую все методы и другие детали, можно найти здесь;
- В gosli также есть все эти методы для базовых типов Golang из коробки, они описаны здесь.
Кроме того, каждый метод типов -Slice также возвращает такой тип, поэтому методы можно объединить в такую цепочку:
result, err := SomeTypeSlice(original). Where(filter1). Where(filter2). Page(1, 2)
Итак, спасибо за чтение. Если вы хотите проверить gosli, не стесняйтесь спрашивать меня об этом или создавать проблемы в репозитории.
В папке эксперимент есть несколько тестов, тестов и примеров, которые, я надеюсь, помогут понять, как использовать библиотеку в ваших приложениях.
Кроме того, вы можете найти другие потрясающие изображения сусликов (например, те, которые я использовал в этой статье) от Эгона Эльбра под лицензией CC0 здесь https://github.com/egonelbre/gophers