как избежать повторения в последовательной обработке ошибок - PullRequest
0 голосов
/ 05 мая 2018

У меня есть следующая обработка последовательных ошибок:

nodes, err := model.AllNodes()
if err != nil {                           // This error handling  
    pr := progressRes{                    // is Just a copy of
        Type:        progressResponse,    // error handling
        Message:     err.Error(),         // for the next error,
        MessageType: errorMessage,        // what is the
        Progress:    100,                 // best practice
    }                                     // to avoid
    go response(ws, pr.json())            // repeating myself here
    return                                // without making the code
}                                         // complicated
links, err := model.AllLinks()
if err != nil {
    pr := progressRes{
        Type:        progressResponse,
        Message:     err.Error(),
        MessageType: errorMessage,
        Progress:    100,
    }
    go response(ws, pr.json())
    return
}

Какова лучшая практика, чтобы не повторяться в приведенном выше коде, не делая код сложным? Я могу подумать о добавлении нового func, однако я просто подумал, что может быть лучшая идея, о которой я не знаю.

Ответы [ 3 ]

0 голосов
/ 05 мая 2018

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

Отсрочка будет работать так:

// All of these must be defined before we call defer.
var err error
var progressResponse string
var errorMessage string
defer func() {
    // Since the deferred function will be called even if
    // the function has completed successfully, we need to
    // check that there actually has been an error before we
    // create the error response.
    if err == nil {
        return
    }
    pr := progressRes{
        Type:        progressResponse,
        Message:     err.Error(),
        MessageType: errorMessage,
        Progress:    100,
    }
    go response(ws, pr.json())
}()
nodes, err := model.AllNodes()
if err != nil {
    // The deferred function will automatically be called here.
    return
}
links, err := model.AllLinks()
if err != nil {
    // And in every other place where the function returns.
    return
}

Проблема здесь в том, что могут быть некоторые подводные камни, связанные с изменением теней. Возьмите этот пример:

var err error
defer func() {
    if err != nil {
        handleError(err)
    }
}()
if a, err := doSomething(); err != nil {
    return
}

Вот подтверждение концепции на детской площадке .

Проблема здесь в том, что err внутри предложения if не совпадает с тем, что в верхней области видимости; если вы также объявляете a в верхней области и используете одно присваивание = вместо объявления :=, то оно работает как ожидалось . Затенение переменных является распространенной ошибкой, особенно для начинающих; дальнейшее чтение .

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

func a() {
    links, err := b()
    if err != nil {
        pr := progressRes{
            Type:        progressResponse,
            Message:     err.Error(),
            MessageType: errorMessage,
            Progress:    100,
        }
        go response(ws, pr.json())
        return
    }
}

func b() ([]Link, error) {
    nodes, err := model.AllNodes()
    if err != nil {
        return nil, err
    }
    links, err := model.AllLinks()
    if err != nil {
        return nil, err
    }
    // you probably then do something with nodes and links?
    return resolveLinks(nodes, links)
}
0 голосов
/ 06 мая 2018

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

func doStuff() {
    if err := doStuffWithError(); err != nil {
        pr := progressRes{
            Type:        progressResponse,
            Message:     err.Error(),
            MessageType: errorMessage,
            Progress:    100,
        }
        go response(ws, pr.json())
        return
    }
}

func doStuffWithError() error {
    nodes, err := model.AllNodes()
    if err != nil {
        return err
    }
    links, err := model.AllLinks()
    if err != nil {
        return err
    }
    // Do something with nodes and links
    return nil
}
0 голосов
/ 05 мая 2018

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

Один вариант:

func allNodesAndLinks() ([]*Node, []*Link, error) {
    nodes, err := model.AllNodes()
    if err != nil {
        return nil, nil, err
    }
    links, err := model.AllLinks()
    if err != nil {
        return nil, nil, err
    }
    return nodes, links, nil
}

// ...

nodes, links, err := allNodesAndLinks()
if err != nil {
    pr := progressRes{
        Type:        progressResponse,
        Message:     err.Error(),
        MessageType: errorMessage,
        Progress:    100,
    }
    go response(ws, pr.json())
    return
}

Другой вариант:

func respondWithError(ws io.Writer, err error) {
    pr := progressRes{
        Type:        progressResponse,
        Message:     err.Error(),
        MessageType: errorMessage,
        Progress:    100,
    }
    response(ws, pr.json())
}

// ...

nodes, err := model.AllNodes()
if err != nil {
    go respondWithError(ws, err)
    return
}
links, err := model.AllLinks()
if err != nil {
    go respondWithError(ws, err)
    return
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...