"Как не утонуть в ошибках в Go" - Илья Шихалеев, iSpring
Вставка
- Опубліковано 10 січ 2019
- Небольшой доклад об обработке ошибок в языке программирования Go и о том, как писать бизнес-логику на Go, не утонув в if err != nil.
Презентация: speakerdeck.com/quasilyte/kak... - Наука та технологія
А в итоге плодить море функций. Спасибо за способ.
Я пложу не море функций, а один раз написал наборы стандартных функций для обработки ошибок со своими понятными названиями и просто использую их как библиотеку во всех своих приложениях. Получается - прекрасно.
Если честно люди которые там по ходу повествования встревали со своими "ну очень важными уточнениями" вели себя крайне неуважительно для вопросов есть время в конце доклада а не по середине.
Я просто написал библиотеку функций, внутри которых встроены все необходимые проверки ошибок и их обработка, и в своих программах вызываю уже мои функции. В результате код получается коротким и чистым - без всяких там +100500 if err... Очень удобная штука.
Теоретически можно даже такую стандартную библиотеку написать для всех функций го, требующих обработки ошибок и выложить как заплатку на гитхаб, да токо лень этим заниматься - никто ведь даже спасибо не скажет... так что пока только для себя и только то, что нужно в работе.
Спасибо за ваш комментарий! Было бы интересно посмотреть на ваше решение, даже не обобщённое :) Если выложите на гитхаб - скиньте ссылку, пожалуйста :)
@@iSpringTech Да какое это решение... просто здравый смысл. Код же невозможно нормально читать и воспринимать, когда на каждом шагу эти сосиски из:
if err != nil {
...to do something
fmt.Println("
Error: ", s, err)
return
}
Поэтому делаю просто:
Создаю в корневом src папочку libs - это пакет основных библиотек. В этом пакете создают несколько файлов, которые содержат целевые функции. Например, все функции, которые связаны с обработкой файлов лежат в файле file.go, функции для конвертирования типов данных в файле conv.go и так далее. Те функции, которые относятся ко всему сразу или не относятся ни к чему, ложу в общий файл называемый base.go.
Дальше, к каждому из таких файлов создаю парный файл file_err.go, conv_err.go и так далее.
В функциональный файл ложу функции, которые выполняют конкретную задачу. Возьмем к примеру "создать файл". Вот эта функция из файла file.go.:
// Процедура для создания файла
func FIL_creatFile(s_path string, s_name_ext string) {
var s_path_name_ext string
if s_name_ext == "" {
fmt.Println("file name not specified")
return
} else {
s_path_name_ext = s_path + s_name_ext
ERR_creatFile(s_path_name_ext)
}
}
Как видите, основная задача в этой функции передается на функцию ERR_creatFile, которая находится в file_err.go, вот она:
func ERR_creatFile(s_path_name_ext string) {
file, err := os.Create(s_path_name_ext)
ERR_printError("Unable to create file:", err)
file.Close()
}
Эта фукнция уже является обработчиком ошибки и в нее можно вставить то, что Вам нужно для конкретной задачи. В данном случае я никак не обрабатываю эту ошибку, а просто вывожу соощение о ней, так как в данный момент у меня рабочие процессы и никакой другой обработки мне не нужно.
Функции типа ERR_printError, которые выполняют реактивные задачи обработки ошибок, лежат в общем файле errors.go.
Вот например эта:
// Процедура демонстрации ошибки
func ERR_printError(s string, err error) {
if err != nil {
fmt.Println("
Error: ", s, err)
return
}
}
Получается очень удобный библиотечный пакет, в котором хорошо понятно где что находится. Префиксы в коде позволяют быстро находить нужные функции и добавлять в них функционал если что нужно. Чтобы эти глобальные функции отличались от локальных функций каждого проекта, я в них префиксы пишу только заглавными буквами, а в локальных пакетах заглавной делаю только первую букву.
Например. В базовой библиотеке функция создания файла начинается с FIL_, она стандартна и подходит для любых проектов. Ее задача - скрыть в себе обработчик ошибок. Если же в проекте мне нужна какая-то особая функция работы с файлами, например, запись в файл хэша файла, то я создаю в проете локальный пакет с библиотеками, где есть такой же файл file.go, но в нем функции начинаются с Fil_. Так я их различаю.
Базовую библиотеку всегда подключаю через точку, это позволяет пользоваться этими функциями без дополнительных обозначений.
В результате вместо сосисок, мой код выглялит как-то так:
// Открываем файл для чтения
file_reading := FIL_openReadFile(s_path_name_ext)
defer file_reading.Close()
// получаем статистику файла
var i64_size_file int64
stat := FIL_getStatFile(file_reading)
i64_size_file = stat.Size() // Получаем размер файла
// Разделяем s_path_name_ext на составляющие
var s_path string
var s_name string
var s_ext string
s_path, s_name, s_ext = FIL_splitPathNameExt(s_path_name_ext)
// Сокращаем если нужно полное имя файла
var s_name_ext string
s_name_ext = s_name + "." + s_ext
s_name_ext = FIL_cutNameFile(s_name_ext)
// Добавляем расширение шифрованного файла
s_name_ext = s_name_ext + ".cip"
s_path_name_ext = s_path + s_name_ext
// Проверяем существует ли файл
bo := FIL_existsDirFile(s_path_name_ext)
if bo == false {
// Создаем файл для записи шифра
FIL_creatFile(s_path, s_name_ext)
} else {
// Очищаем файл
FIL_zeroingFile(s_path_name_ext, 0)
}
// Открываем файл для записи
file_writing := FIL_openWriteFile(s_path_name_ext, Cs_perm)
defer file_writing.Close()
Код рабочий, рефакторинг еще не проводился, так что не удивляйтесь...
НО, представьте себе, как будет выглядеть и насколько растянется вся эта констуркция - будь в ней сосиски...
@@iSpringTech Теперь что касается создания некой базовой библиотеки обработки ошибок, которая вполне может спасти кучу нервов многим программистам, если им понравится такой подход.
Во-первых, эту библиотеку нужно написать универсальной, то есть - она должна стать ОБЕРТКОЙ для всех фукнций стандартной библиотеки Go, которые имеют те или иные обработчики ошибок.
Второе - эти обработчики должны содержать все возможные стардартные варианты обработки, а не так как у меня - только то, что мне нужно в данный момент.
Третье - каждая функция должна содержать подключаемый интерфейс, который будет позволять пользователю подключать свои собственные обработчики, которые ему нужны в каких-то специфических задача.
Ну и четвертое - каждая фукнция этой библиотеки должна иметь флаг, который позволяет включать или отключать те или иные обрабтчики - это нужно для различных режимов работы с кодом. Например, если я только начинаю программу, то мне ничего кроме вывода ошибки на печать не нужно и я из коробки должен иметь возможность установить при подаче данных в функцию на флаге какую-нибудь "1", что будет означать, что функция будет только выводить ошибку и никак ее не обрабатывать, ну или "0", если мне вообще ничего от нее не нужно... А уже в рабочем режиме ее можно запускать на полную катушку, ну или в продакшин.
Итого, как Вы видите - это отдельный огромный кусок работы, который кто-то должен просто сесть, сделать, протестить и выложить всем на спасибо. И я бы рад это сделать, кабы мне так же за спасибо все давали - одежду, жилье, транспорт, пищу... Но не дают - все требуют деньог.
Так что покамест добрыми делами сыт не будешь, я вынужден тратить время на то, за что можна хотя бы что-то взять с других людей.
Такие-то дела...
@@flamehowk А как в вашем примере гарантируется, что файл создан? Или вызывающий код не контролирует ошибочных операций и они только в stdout логируются?
@@flamehowk В итоге, если я правильно понял, ваше видение обобщённой библиотеки превращает её в набор гибко настраиваемых функций, с передачей флагов и обработчиков ошибок. В стандартном подходе, где обработка остаётся на уровне вызывающего кода, получается похожий подход, но без вашей обёртки. В таком случае не понимаю необходимости предлагаемого решения. В го ошибки - это значения и вызывающий код обязан их обработать. Да, для бизнес логики можно перенести/спрятать обязанность обработки ошибок в структуры/функции. Обобщённое решение хорошо подходит для обобщённых компонентов, как работа с файлами в вашем примере. Плюс есть ситуация с необходимостью накопления ошибки. Ваше решение возможно хорошо подходит для ваших задач, но как его в итоге обобщить и в чём будет итоговая выгода - я не понял.
Дайте слайды пожалуйста
Без проблем, ловите :) speakerdeck.com/quasilyte/kak-nie-utonut-v-oshibkakh
@@iSpringTech оу май ..) спасибо
В середине 70-х годов на сельском клубе появляется объявление:
"Лекция "Виды любви". Лекция со слайдами."
На лекцию приходит вся деревня.
На трибуну выходит лектор и начинает:
Первый вид любви - это любовь мужчины к женщине.
Народ: Слайды! Слайды!
Лектор: Слайды будут позже.
Второй вид любви - это любовь мужчины к мужчине.
Народ: Слайды! Слайды!
Лектор: Слайды будут позже.
Третий вид любви - это любовь женщины к женщине.
Народ: Слайды! Слайды!
Лектор: А ещё, товарищи, существует такой вид любви как любовь советского человека к своей Родине. ВОТ ТЕПЕРЬ БУДУТ СЛАЙДЫ!