一些关于Go法度圭表类型偏向措置的相干建议

序言

Go的关于圭表干建偏向措置这块是往常被大年夜师吐槽较多的中心,我在工作中也不雅不雅不雅不雅不雅不雅不雅不雅不雅不雅不雅不雅不雅不雅不雅不雅查询拜访到一些气候 ,法度斗劲严重的类型是在各层级的逻辑代码中对所长的措置有些几回 。

比如 ,措置有人写代码就会在每层都剖断偏向并记实日记,关于圭表干建从代码层面看,法度貌似很严谨,类型可是措置假定看日记会创作创造一堆几回的信息,比及排盘考题时反而会构成烦扰。关于圭表干建

往日给大年夜师总结三点Go代码偏向措置相干的法度最好幻想给大年夜师 。

这些最好幻想也是类型网上一些先辈分享的 ,我本身幻想后在这里用本身的措置言语描摹出来 ,希看能对大年夜师有所辅佐。关于圭表干建

熟谙error

Go法度圭表类型经由过程error圭表类型的法度值展示偏向

error圭表类型是一个内建接口圭表类型 ,该接口只端方了一个前去字符串值的类型Error编制。

type error inte***ce {     Error() string}

Go言语的函数经常会前去一个error值 ,调用者经由过程测试error值可否是nil来举办偏向措置。

i, err := strconv.Atoi("42")if err != nil {     fmt.Printf("couldn't convert number: %v\n", err)    return}fmt.Println("Converted integer:", i)

error为nil时展示成功;非nil的error展示损掉落踪落败 。

自定义偏向记得要完成error接口

我们经常会定义契合本身需求的偏向圭表类型 ,可是记住要让这些圭表类型完成error接口,多么就不必在调用方的法度圭表类型里引进出格的圭表类型 。

比以上面我们本身定义了myError这个圭表类型,假定不完成error接口的话 ,调用者的代码中就会被myError这个圭表类型侵进 。比以上面的run函数 ,在定义前去值圭表类型时,直接定义成error便可 。

package myerrorimport ( "fmt" "time")type myError struct {  Code int When time.Time What string}func (e *myError) Error() string {  return fmt.Sprintf("at %v, %s, code %d",  e.When, e.What, e.Code)}func run() error {  return &MyError{     1002,    time.Now(),    "it didn't work", }}func TryIt() {  if err := run(); err != nil {   fmt.Println(err) }}

假定myError不完成error接口的话,这里的前去值圭表类型就要定义成myError圭表类型 。可想而知,紧接着调用者的法度圭表类型里就要经由过程myError.Code == *** 来剖断幻想下场是哪一种具体的偏向(当然想要这么干得先把myError改成导出的MyError) 。

那调用者剖断自定义error是具体哪一种所长的时辰理应若何办呢 ,myError并未向包外泄漏,谜底是经由过程向包外泄漏搜检偏向行动的编制来完成 。

myerror.IsXXXError(err) ...

抑或是经由过程斗劲error本身与包向外泄漏的常量偏向可否相当来剖断,比如独霸文件经经常独霸来剖断文件可否停止的io.EOF 。

近似的另有gorm.ErrRecordNotFound等各类开源包对外泄漏的偏向常量 。

if err != io.EOF {     return err}

偏向措置常犯的偏向

先看一段复杂的法度圭表类型  ,看大年夜师能不克不及创作创造一些纤细的问题

func WriteAll(w io.Writer, buf []byte) error {     _, err := w.Write(buf)    if err != nil {         log.Println("unable to write:", err) // annotated error goes to log file        return err                           // unannotated error returned to caller    }    return nil}func WriteConfig(w io.Writer, conf *Config) error {     buf, err := json.Marshal(conf)    if err != nil {         log.Printf("could not marshal config: %v", err)        return err    }    if err := WriteAll(w, buf); err != nil {         log.Println("could not write config: %v", err)        return err    }    return nil}func main() {     err := WriteConfig(f, &conf)    fmt.Println(err) // io.EOF}

偏向措置常犯的两个问题

上面法度圭表类型的偏向措置泄漏了两个问题 :

1.底层函数WriteAll在产生发火偏向后 ,除向基层前去偏向外还向日记里记实了偏向 ,基层调用者做了一样的工作 ,记实日记然后把偏向再前去给法度圭表类型顶层。

是以在日记文件中掉落踪掉落踪一堆几回的内容

unable to write: io.EOF
could not write config: io.EOF
...

2. 在法度圭表类型的顶部  ,当然掉落踪掉落踪了原始偏向,但没有相干内容,换句话说没有把WriteAll、WriteConfig记实到 log 里的那些信息包装到偏向里 ,前去给基层。

针对这两个问题标措置筹划可所以,在底层函数WriteAll 、WriteConfig中为产生发火的偏向添加凹凸文信息 ,然后将偏向前去基层,由基层法度圭表类型末尾措置这些偏向 。

一种复杂的包装所长的编制是独霸fmt.Errorf函数 ,给偏向添加信息 。

func WriteConfig(w io.Writer, conf *Config) error {     buf, err := json.Marshal(conf)    if err != nil {         return fmt.Errorf("could not marshal config: %v", err)    }    if err := WriteAll(w, buf); err != nil {         return fmt.Errorf("could not write config: %v", err)    }    return nil}func WriteAll(w io.Writer, buf []byte) error {     _, err := w.Write(buf)    if err != nil {         return fmt.Errorf("write failed: %v", err)    }    return nil}

给偏向附加凹凸文信息

fmt.Errorf只是给偏向添加了复杂的注解信息,假定你想在添加信息的同时还加上所长的调用栈 ,可以借助github.com/pkg/errors这个包供给的偏向包装身手   。

//只附加新的信息func WithMessage(err error, message string) error//只附加调用仓库信息func WithStack(err error) error//同时附加仓库和信息func Wrap(err error, message string) error

有包装编制,就有对应的解包编制 ,Cause编制会前去包装偏向对应的最原始偏向--即会递回地举办解包。

func Cause(err error) error

上面是独霸github.com/pkg/errors改写后的偏向措置法度圭表类型

func ReadFile(path string) ([]byte, error) {     f, err := os.Open(path)    if err != nil {         return nil, errors.Wrap(err, "open failed")    }    defer f.Close()    buf, err := ioutil.ReadAll(f)    if err != nil {         return nil, errors.Wrap(err, "read failed")    }    return buf, nil}func ReadConfig() ([]byte, error) {     home := os.Getenv("HOME")    config, err := ReadFile(filepath.Join(home, ".settings.xml"))    return config, errors.WithMessage(err, "could not read config")}func main() {     _, err := ReadConfig()    if err != nil {         fmt.Printf("original error: %T %v\n", errors.Cause(err), errors.Cause(err))        fmt.Printf("stack trace:\n%+v\n", err)        os.Exit(1)    }}

上面格式化字符串时用的 %+v 是在 % v 根本上,对值举办展开,即展开复合圭表类型值,好例如案体的字段值等明细 。

多么既能给偏向添加调用栈信息 ,又能保管对原始所长的援引 ,经由过程Cause可以恢复到最初始激起所长的启事 。

总结

总结一下 ,偏向措置的绳尺就是 :

偏向只在逻辑的最外层措置一次,底层只前去偏向 。
底层除前去偏向外,要对原始偏向举办包装,添加偏向信息 、调用栈等这些无益于排查的凹凸文信息 。

到此这篇关于Go法度圭表类型偏向措置的相干建议的文章就引见到这了,更多相干Go法度圭表类型偏向措置内容请搜刮完竣下载之前的文章或延续不雅不雅不雅不雅鉴赏上面的相干文章希看大年夜师往后多多支撑完竣下载 !

上一篇:舒适精英黑刃奇妙若何打
下一篇:光遇小溪旁的石室在何处

欢迎扫描关注我们的微信公众平台!

欢迎扫描关注我们的微信公众平台!