Попробовал немного рифмовать в названии. Не принимайте всерьез слово «за».
Для разработчика, привыкшего к ключевому слову class
, Go или Golang (как его часто называют) — это что-то вроде отключения. Объектов нет, но struct
. Нет наследования, есть композиция. Иерархии исключений нет, но error
. Есть pointer
или их отсутствие. Параллелизм не одобряется (что!). В этой статье я намерен обсудить некоторые ловушки, с которыми сталкиваются опытные разработчики, пытающиеся изучить Go и стать продуктивнее.
Кто переместил мои объекты
Я часто задавался вопросом, почему создатели Go отказались от объектов и отдали предпочтение struct
. Можно подумать, что в промежуточном байтовом коде нет Объектов, тогда зачем они в самом коде. Посмотрите на класс Animal
, который имеет два поля и два метода.
В машинном коде он транслируется в разные части памяти. Излишне говорить, что память команд повторно используется в разных экземплярах class Animal
.
Итак, как мы видим, поля в классе транслируются в память, а методы attached
в поля выполняют какое-то действие. Все методы получают указатель this
, который может манипулировать данными. Почти все языки объектно-ориентированного программирования переписывают методы, чтобы включить указатель this
. Это объектно-ориентированное программирование с высоты птичьего полета.
Теперь вы можете понять, почему struct
имеет большой смысл. Вы определяете поля в struct
, а затем применяете к ним методы attach
с помощью receiver
. Вы могли заметить, что в Go указатель this
заменяется явно присоединенным receiver
— тем, который входит в (a Animal)
.
Написать вместо наследования
Одним из основных преимуществ ООП было наследование, несмотря на его недостатки. Основным аргументом было повторное использование кода. Допустим, у нас есть новый класс Lion
, который добавляет дополнительное поле home
. Но мне нужны другие методы в Animal
. Естественно, вы extend
переопределите методы, которые, по вашему мнению, могут не относиться к базовому классу.
Теперь, если цель состоит в том, чтобы просто получить методы суперкласса, почему бы просто не использовать повторно функции, определенные как есть. Это именно тот способ, которым Go делает это. Переписывание на Go
Составив Animal
в строке 3, вы бесплатно получите все его методы и члены. Это очень странная нотация, но она делает именно то, что нам нужно. Также инициализация структуры немного более странная (сначала). Но потом, если вы думаете, что это compose
, тогда все начинает обретать смысл.
Исключительно??
У меня проблема с тем, как java обрабатывает Exception
. На первый взгляд выглядит безобидно. Нам дана иерархия исключений, где мы можем создавать всевозможные исключения и бросать их в наш код. Все идет нормально. Но что, если мы воспользуемся библиотекой. Они решили пошалить со своими исключениями, и внезапно мы, как потребители, вынуждены обрабатывать все эти исключения, создавая бесконечные catch
. Код, подобный приведенному ниже, не редкость в кодовой базе Java.
Конечно, есть много синтаксического сахара и всемирно известных @SneakyThrows
для облегчения проблемы, но проблемы не должно было быть в первую очередь. Моя главная проблема с обработкой некоторого случайного исключения из другой библиотеки заключается в том, что оно нарушает abstraction
, одно из обещаний ООП. Упс!!
Введите error
. Go очень подробно описывает обработку ошибок. В Go идиоматично обрабатывать ошибки тут и там. Не оставляйте нисходящий поток для обработки ваших ошибок. Следовательно, обработка ошибок, как показано ниже, является нормой в Go. Негласное соглашение состоит в том, чтобы последний возвращаемый параметр в func
был error
, если он его выдает.
указатели
Нравится вам это или нет, в Java есть указатели, но тонкие. В Go есть возможность явных указателей, если это необходимо. Обычно, когда вы передаете структуры в Go, они копируются, как primitive
в Java. Это означает, что объекты копируются в стек. Полученная программа, вероятно, работает быстрее, поскольку циклы процессора быстрее, но доступ к памяти - нет.
В Java все объекты создаются в куче или в памяти, это означает, что мы передаем ссылку (указатель, если хотите) на объект, что дает возможность вызываемому объекту изменять объект. В Go вам придется дать эту возможность, объявив об обходе указателя. Вам не нужно передавать указатель, если вам не нужно изменить рассматриваемый объект.
Это приводит к шаблону наличия pointer receiver
в некоторых случаях, когда вы действительно хотите изменить объект.
параллелизм
Я люблю параллелизм и использую его как инструмент, чтобы сделать мои программы более эффективными (по крайней мере, на мой взгляд, реальность другая). Дело в том, что параллелизм очень сложно сделать правильно, и часто, когда что-то идет не так, это происходит в продакшене! В Go есть все возможности параллелизма, которые есть в Java, за исключением шумихи. Но снова использование не одобряется. Вместо этого Go предлагает использовать channel
. По сути, это примитив в Go, где вы можете писать и читать.
Рассмотрим эту старую добрую программу производитель-потребитель. Производитель(и) производит, а некоторые потребители потребляют. Между ними обычно находится LinkedBlockingQueue
, который обрабатывает буфер элементов, произведенных для потребления. В Go это канал с типом chan
. Программа ниже показывает несколько вещей, а именно. использование канала для буферизации входящих и исходящих данных, шаблон канала done
и подпрограммы Go, которые эквивалентны Thread
(почти), вызываемому с go doSome()
Управление пакетами
Если вы не хотите постоянно собирать каждый бит кода до уровня byte
, вам нужно будет использовать библиотеку — желательно с открытым исходным кодом — для выполнения этой работы. В большинство современных языков программирования встроено хорошее управление пакетами. Голанг не исключение. Go использует немного другой подход, когда дело доходит до управления пакетами.
Для библиотек нет repository
. В Java у нас есть репозиторий maven
. На узле есть репозиторий npm
. Go автоматически индексирует библиотеки, загруженные в системы контроля версий, такие как github
, и делает их доступными в https://pkg.go.dev/. Он делает некоторую оптимизацию для пометки версии и, следовательно, поддерживает копию, но pkg.go.dev
следует рассматривать как документацию, а не репозиторий. Я должен предупредить вас, что все управление модулями в Go очень самоуверенно.
Заключение
У Go есть свои сильные и слабые стороны. Я не думаю, что существует какой-то один язык программирования, отвечающий всем требованиям, всегда есть подвох. Если в этой статье вы задались вопросом, использовать ли Go в своем следующем проекте, то вы можете быть разочарованы. Я уверен, что вы знаете свой проект и можете принять взвешенное решение.
Для меня простота Go — самый интересный элемент. Исходя из фона Java, я привык к бесконечному количеству библиотек и их аннотаций повсюду только для простой сериализации. Все это часто упрощается в Go, который предоставляет мощные абстракции для разработки современного кода с гораздо меньшим объемом памяти, не отказываясь от производительности.