亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

從多個抽象級別處理錯誤的最佳實踐

從多個抽象級別處理錯誤的最佳實踐

Go
繁花不似錦 2022-01-10 17:33:06
我想知道在 go 中處理多級抽象錯誤的最佳方法是什么。每次如果我必須為程序添加一個新的抽象級別,我就不得不將錯誤代碼從低級別傳輸到高級別。因此日志文件中有重復的通信,否則我必須記住刪除通信表級別低并將他轉移到更高級別。下面簡單舉例。我跳過創建每個對象來更短和更清晰的代碼,但我認為你理解我的問題type ObjectOne struct{    someValue int}func (o* ObjectOne)CheckValue()error{    if o.someValue == 0 {        SomeLogger.Printf("Value is 0 error program") // communicate form first level abstraction to logger        return errors.New("Internal value in object is 0")    }    return nil}type ObjectTwoHigherLevel struct{    objectOne ObjectOne}func (oT*  ObjectTwoHigherLevel)CheckObjectOneIsReady() error{    if err := oT.objectOne.CheckValue() ; err != nil{        SomeLogger.Printf("Value in objectOne is not correct for objectTwo %s" , err) //  second communicate        return  err    }    return nil}type ObjectThreeHiggerLevel struct{    oT ObjectTwoHigherLevel}func (oTh* ObjectThreeHiggerLevel)CheckObjectTwoIsReady()error{    if err := oTh.oT.CheckObjectOneIsReady() ; err != nil{        SomeLogger.Printf("Value in objectTwo is not correct for objectThree %s" , err)    return err    }    return nil}結果在日志文件中我得到重復的帖子Value is 0 error program Value in objectOne is not correct for objectTwo Internal value in object is 0 Value in objectTwo is not correct for objectThree Internal value in object is 0反過來,如果我只將一些轉移err到更高級別而沒有額外的日志,我會丟失每個級別發生的信息。這個怎么解決?隱私副本如何通信?還是我的路是好的也是唯一的?如果我創建一些對象,這些對象在幾個抽象級別上搜索數據庫中的某些內容,那么問題會更加令人沮喪,然后我在 logFile 中也得到幾行形成相同的任務。
查看完整描述

2 回答

?
蕪湖不蕪

TA貢獻1796條經驗 獲得超7個贊

編輯:這個答案早于 Go 1.13,它提供了與所介紹的技術類似的東西。請查看Go 博客:處理 Go 1.13 中的錯誤。


您應該處理錯誤,或者不處理錯誤,而是將其委托給更高級別(給調用者)。處理錯誤并返回它是不好的做法,就好像調用者也這樣做一樣,錯誤可能會被處理多次。


處理錯誤意味著檢查它并根據它做出決定,這可能是您簡單地記錄它,但這也算作“處理”它。


如果您選擇不處理而是將其委托給更高級別,那可能很好,但不要只返回您得到的錯誤值,因為它可能對沒有上下文的調用者毫無意義。


注釋錯誤

一個非常好的和推薦的委派方式是Annotating errors。這意味著您創建并返回一個新的錯誤值,但舊的錯誤值也包含在返回值中。包裝器為包裝的錯誤提供上下文。


沒有為標注錯誤公共圖書館:github.com/pkg/errors; 及其godoc:errors


它基本上有 2 個功能: 1 用于包裝現有錯誤:


func Wrap(cause error, message string) error

還有一個用于提取包裝錯誤:


func Cause(err error) error

使用這些,您的錯誤處理可能如下所示:


func (o *ObjectOne) CheckValue() error {

    if o.someValue == 0 {

        return errors.New("Object1 illegal state: value is 0")

    }

    return nil

}

第二級:


func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {

    if err := oT.objectOne.CheckValue(); err != nil {

        return errors.Wrap(err, "Object2 illegal state: Object1 is invalid")

    }

    return nil

}

第三級:僅調用第二級檢查:


func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {

    if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {

        return errors.Wrap(err, "Object3 illegal state: Object2 is invalid")

    }

    return nil

}

請注意,由于這些CheckXX()方法不處理錯誤,因此它們不會記錄任何內容。他們正在委派帶注釋的錯誤。


如果有人使用ObjectThreeHiggerLevel決定處理錯誤:


o3 := &ObjectThreeHiggerLevel{}

if err := o3.CheckObjectTwoIsReady(); err != nil {

    fmt.Println(err)

}

將呈現以下漂亮的輸出:


Object3 illegal state: Object2 is invalid: Object2 illegal state: Object1 is invalid: Object1 illegal state: value is 0

沒有多個日志的污染,并且所有細節和上下文都被保留了,因為我們使用errors.Wrap()它產生一個錯誤值,該錯誤值格式化為 a string,它遞歸地保留包裝的錯誤:錯誤堆棧。


您可以在博客文章中閱讀有關此技術的更多信息:


Dave Cheney:不要只檢查錯誤,要優雅地處理它們


“擴展”錯誤

如果您喜歡更簡單的事情和/或您不想與外部庫發生麻煩并且您無法提取原始錯誤(確切的錯誤值,而不是您可以的錯誤字符串),那么您可以只需使用上下文擴展錯誤并返回這個新的擴展錯誤。


擴展錯誤最容易通過使用fmt.Errorf()它來完成,它允許您創建一個“漂亮”的格式化錯誤消息,它會返回一個類型的值,error因此您可以直接返回它。


使用fmt.Errorf(),您的錯誤處理可能如下所示:


func (o *ObjectOne) CheckValue() error {

    if o.someValue == 0 {

        return fmt.Errorf("Object1 illegal state: value is %d", o.someValue)

    }

    return nil

}

第二級:


func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {

    if err := oT.objectOne.CheckValue(); err != nil {

        return fmt.Errorf("Object2 illegal state: %v", err)

    }

    return nil

}

第三級:僅調用第二級檢查:


func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {

    if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {

        return fmt.Errorf("Object3 illegal state: %v", err)

    }

    return nil

}

ObjectThreeHiggerLevel如果它決定“處理”它,將顯示以下錯誤消息:


o3 := &ObjectThreeHiggerLevel{}

if err := o3.CheckObjectTwoIsReady(); err != nil {

    fmt.Println(err)

}

將呈現以下漂亮的輸出:


Object3 illegal state: Object2 illegal state: Object1 illegal state: value is 0

請務必閱讀博文:錯誤處理和 Go


查看完整回答
反對 回復 2022-01-10
?
侃侃爾雅

TA貢獻1801條經驗 獲得超16個贊

有各種庫將堆棧跟蹤嵌入到 Go 錯誤中。只需使用其中之一創建錯誤,它就會冒出完整的堆棧上下文,您可以稍后檢查或記錄。

一個這樣的圖書館:

https://github.com/go-errors/errors

還有一些我忘記了。


查看完整回答
反對 回復 2022-01-10
  • 2 回答
  • 0 關注
  • 222 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號