Недавно я перешел от работы над объектно-ориентированными языками программирования (Java и Kotlin) к работе над GoLang.

Изначально я хотел спроектировать свое приложение, как раньше делал в Java, все было объектом, зависимости объектов были некоторыми другими объектами. Эти объекты демонстрируют некоторое поведение с помощью методов. Обычно у меня были интерфейсы, которые реализовывались классами (структурами). А затем я написал несколько фабричных функций для создания объектов классов.

Когда я попытался сделать то же самое в GoLang, это не показалось идиоматическим, и это не было в том же стиле, что и код стандартной библиотеки 😕. Что я делаю не так? Итак, я попытался понять философию дизайна в GoLang.

Прочитав несколько текстов о ней (в основном авторомBill Kennedy of Ardan Labs). Я лучше понимал философию дизайна GoLang.

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

Это первая часть серии. Следующие части серии:

1. Функции более читабельны, чем методы.

В большинстве языков ООП мы используем методы (привязанные к объекту или классу), а не функции (не привязанные к объекту или классы). На большинстве языков ООП также проще работать с методами, а не с функциями.

Но GoLang предлагает нам обе функции и методы, и стандартная библиотека имеет множество функций и несколько методов. Мы часто сталкиваемся с дилеммой, что мы должны использовать, методы или функции.

Давайте начнем с понимания того, какая разница, используем ли мы функцию или метод?

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

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

С помощью метода мы можем использовать все поля, включенные в структуру получателя, или ее подмножество. Что может привести к некоторому загрязнению кода. Методы, которым требуется только подмножество полей, имеют доступ и к другим полям.

Давайте посмотрим на пример, иллюстрирующий это,

В большинстве языков ООП мы используем классы для логической группировки некоторого поведения, например, в следующем примере UserRepository, где я могу читать и записывать Users из источника. Итак, вот как я преобразовал свой java-код в GoLang. Я привязал поведение к структуре.

Есть ли проблемы с читабельностью в приведенном выше коде?

Метод Read() использует только reader, но effectively принимает DefaultUserRepository в качестве параметра. Которая содержит как reader, так и writer. Это то, что мы не можем определить из определения метода, поэтому читаемость скомпрометирована.

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

Как решить эту проблему?

Если нам нужно логически сгруппировать некоторые функции, нам не нужно привязывать их к структуре, мы можем использовать пакеты в Go для таких случаев использования.

Вот так выглядит мой код, если я использую пакеты вместо методов.

Это более читабельно. Из сигнатуры функции ясно, что мы будем использовать только reader в функции Read() и writer в функции Write().

Есть больше преимуществ в использовании функций,

Каждый пакет в go скомпилирован как библиотека и может использоваться путем импорта пакета. Go не допускает циклический импорт любых двух пакетов. (как модули в java).
Разработчики должны рассматривать каждый пакет как библиотеку, которая обеспечивает определенное поведение или операции на основе предоставленных ему данных.

BottomLine:

Структуры в Go следует использовать только для хранения данных, а не для логической группировки поведения.

Методы следует использовать только тогда, когда мы работаем с данными, которые хранятся в структуре получателя.

Вместо этого пакеты Behavior или Operations могут быть предоставлены пакетами.

Вот ссылка на следующую часть серии