DevGang
Авторизоваться

Golang: поведение при ошибках 

В настоящее время мы используем Errors в go во многих частях кода. Ошибка является важным компонентом Go, а также других языков. Взгляните на интерфейс ошибок в Go:

type error interface {
    Error() string
}

Все просто, правда? это дух Go! Теперь мы рассмотрим несколько сценариев, в которых нам нужно больше, чем строка «содержимого» ошибки:

  1. Ошибки базы данных: ошибка подключения, тайм-аут ввода-вывода, повторяющийся индекс…
  2. Ошибки обслуживания: ошибка с возможностью повторения и без возможности повторения.

У нас может возникнуть наивный подход:

err := SomeFunc()
if err.Error() == "This is a DB connection error" {
    // Handle DB connection error here
}

У этого подхода есть несколько проблем:

  1. Программистам необходимо «запомнить» строку ошибки, скопировать и вставить ее везде в коде всякий раз, когда устанавливается соединение с БД, поэтому его трудно поддерживать, трудно изменить.
  2. Сама строка ошибки длинная и занимает большую часть строк / символов кода в исходном коде, поэтому ее трудно читать.
  3. Не каждая ошибка указывается с постоянной строкой ошибки, они могут смешиваться с некоторыми кодами ошибок, подробностями о контексте, в котором они работают (например, ошибка содержит идентификатор пользователя, чтобы указать, что у пользователя недостаточно прав).

Есть более приемлемый подход, который, как мы видим, используется в исходном коде Go: предопределенный тип ошибки.

// ErrNoRows возвращается сканированием, когда queryrow не возвращает строку
// В таком случае строка запроса возвращает значение строки-заполнителя *, которое
// откладывает эту ошибку до сканирования.
var ErrNoRows = errors.New("sql: no rows in result set")

Каждая ошибка будет начинаться с кода “Err” и сопровождаться именем ошибки. Этот подход совершенно прост и удобен для чтения/понимания/использования. Но это просто обернет текст ошибки в более запоминающуюся переменную, это не могло решить проблему первоначального подхода.

Итак, не думайте о том, как представить тип ошибки в самой переменной ошибки, подумайте о ее значении или поведении: вместо определения типа ошибки соединения с БД, определяет поведение ошибки соединения с БД. Использование подхода ошибочного поведения дает следующие преимущества:

  1. Легко изменить: измените способ устранения ошибки. Поведение - это просто изменение «содержимого» метода, который формирует поведение, это не нарушает поведение, поэтому ничего не отличается от внешнего вида.
  2. Легко расширить: Добавить / удалить поведение при ошибке.
  3. Легко получить доступ к списку поддерживаемых ошибок: обратитесь к списку методов в интерфейсе ошибок, который покажет все поддерживаемые варианты поведения.

Чтобы реализовать поведение ошибки в Go, мы используем интерфейс, есть несколько способов добиться этого, поэтому давайте рассмотрим их и лучший (мое восприятие), который ясен, меньше кода и легко понять последний в список:

Реализуйте методы в интерфейсе ошибки

type NotFoundError interface{ NotFound() }

type serviceError struct {
 err error
}

func (serviceError) NotFound()       {}

func (e serviceError) Error() string { return e.err.Error() }

func NewServiceError(err error) error {
 return serviceError{
  err: err,
 }
}

NotFoundError - это поведение ошибки, а целевая ошибка - serviceError реализует метод NotFound для удовлетворения поведения NotFoundError и реализует метод Error для удовлетворения интерфейса ошибки. Чтобы проверить, является ли ошибка NotFoundError, мы можем использовать два способа:

if _, isNotFound := err.(NotFoundError); isNotFound {}

или

if errors.As(err, new(NotFoundError)) {}

Я имею в виду второй способ.

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

Составить поведение в структуре ошибки

type NotFoundError interface{ NotFound() }

type serviceError struct {
 error
 NotFoundError
}

func NewServiceError(err error) error {
 return serviceError{
  error: err,
 }
}

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

#Golang
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться