Go 具有多个可能失败步骤的结构初始值设定项

Go 具有多个可能失败步骤的结构初始值设定项,go,Go,我有以下基本代码: r1, err := OpenResource() if err != nil { return err; } defer r1.Close() r2, err := OpenResource() if err != nil { return err; } defer r2.Close() r3, err := OpenResource() if err != nil { return err; } defer r3.Close() // Do something wi

我有以下基本代码:

r1, err := OpenResource()
if err != nil { return err; }
defer r1.Close()

r2, err := OpenResource()
if err != nil { return err; }
defer r2.Close()

r3, err := OpenResource()
if err != nil { return err; }
defer r3.Close()

// Do something with r1, r2, r3
...
我想用方法
DoSomething
将其包装成一个结构,我将按如下方式调用它:

s, err := CreateMyStructWithR1R2R3()
if err != nil { return err }
defer s.Close()
s.DoSomethingWithR1R2R3() 
我的第一个实施方法是:

func CreateMyStructWithR1R2R3() (*MyStruct, error) {
  s := MyStruct{}

  r1, err := OpenResource()
  if err != nil { return nil, err; }
  s.r1 = r1

  r2, err := OpenResource()
  if err != nil { r1.Close(); return nil, err; }
  s.r2 = r2

  r3, err := OpenResource()
  if err != nil { r1.Close(); r2.Close(); return nil, err; }
  s.r3 = r3

  return &s
}

func (s *MyStruct) Close() {
   s.r3.Close()
   s.r2.Close()
   s.r1.Close()
}

func (s *MyStruct) DoSomethingWithR1R2R3() { /* ... */ }
但是,当发生错误时,
Create()
函数中的
Close()
调用会让人感觉很难看,并且容易出错

我想到的另一种方法是:

func CreateMyStructWithR1R2R3() (*MyStruct, error) {
  s := MyStruct{}
  success := false

  r1, err := OpenResource()
  if err != nil { return nil, err; }
  defer func() { if !success { r1.Close() } }()
  s.r1 = r1

  r2, err := OpenResource()
  if err != nil { return nil, err; }
  defer func() { if !success { r2.Close() } }()
  s.r2 = r2

  r3, err := OpenResource()
  if err != nil { return nil, err; }
  defer func() { if !success { r3.Close() } }()
  s.r3 = r3

  success = true
  return &s
}
这感觉更安全、更干净,但是bool感觉很难看,而且当Go格式化程序出现时,代码会变得更长


有没有更好的通用模式来确保通过这样的多阶段初始化关闭所有资源?

您可以将
关闭
调用推送到
*MyStruct
关闭

func (s *MyStruct) Close() {
    if s.r3 != nil {
        s.r3.Close()
    }
    if s.r2 != nil {
        s.r2.Close()
    }
    if s.r1 != nil {
        s.r1.Close()
    }
}
并将
CreateMyStructWithR1R2R3的第一个实现更新为:

func CreateMyStructWithR1R2R3() (*MyStruct, error) {
  s := MyStruct{}

  r1, err := OpenResource()
  if err != nil { s.Close(); return nil, err; }
  s.r1 = r1

  r2, err := OpenResource()
  if err != nil { s.Close(); return nil, err; }
  s.r2 = r2

  r3, err := OpenResource()
  if err != nil { s.Close(); return nil, err; }
  s.r3 = r3

  return &s
}

您可以将
Close
调用推送到
*MyStruct
Close

func (s *MyStruct) Close() {
    if s.r3 != nil {
        s.r3.Close()
    }
    if s.r2 != nil {
        s.r2.Close()
    }
    if s.r1 != nil {
        s.r1.Close()
    }
}
并将
CreateMyStructWithR1R2R3的第一个实现更新为:

func CreateMyStructWithR1R2R3() (*MyStruct, error) {
  s := MyStruct{}

  r1, err := OpenResource()
  if err != nil { s.Close(); return nil, err; }
  s.r1 = r1

  r2, err := OpenResource()
  if err != nil { s.Close(); return nil, err; }
  s.r2 = r2

  r3, err := OpenResource()
  if err != nil { s.Close(); return nil, err; }
  s.r3 = r3

  return &s
}

如果将
err
声明为返回参数,则不需要额外的
success
标志,因为
defer
将访问返回的错误值


一种稍微减少重复的方法是引入一种类型来管理
Closer
s的片段。在这个新类型上调用close时,它将在多个
Closer
s上循环,无论您有3个还是300个,然后将它们全部关闭。比如说,像这样:

type Closer interface {
    Close()
}

type multiCloser struct {
    cc []Closer
}

func (mc *multiCloser) add(c Closer) {
    mc.cc = append(mc.cc, c)
}

func (mc *multiCloser) Close() {
    for _, c := range mc.cc {
        c.Close()
    }
}
type MyStruct struct {
    // ...
    mc *multiCloser
}

func (ms *MyStruct) Close() {
    ms.mc.Close()
}

func CreateMyStructWithR1R2R3() (ms *MyStruct, err error) {
    ms = &MyStruct{mc: &multiCloser{}}
    defer func() {
        if err != nil {
            ms.Close()
        }
    }()

    if ms.r1, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r1)

    if ms.r2, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r2)

    if ms.r3, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r3)

    return ms, nil
}
这样,
CreateMyStructWithR1R2R3
的实现如下所示:

type Closer interface {
    Close()
}

type multiCloser struct {
    cc []Closer
}

func (mc *multiCloser) add(c Closer) {
    mc.cc = append(mc.cc, c)
}

func (mc *multiCloser) Close() {
    for _, c := range mc.cc {
        c.Close()
    }
}
type MyStruct struct {
    // ...
    mc *multiCloser
}

func (ms *MyStruct) Close() {
    ms.mc.Close()
}

func CreateMyStructWithR1R2R3() (ms *MyStruct, err error) {
    ms = &MyStruct{mc: &multiCloser{}}
    defer func() {
        if err != nil {
            ms.Close()
        }
    }()

    if ms.r1, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r1)

    if ms.r2, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r2)

    if ms.r3, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r3)

    return ms, nil
}

如果将
err
声明为返回参数,则不需要额外的
success
标志,因为
defer
将访问返回的错误值


一种稍微减少重复的方法是引入一种类型来管理
Closer
s的片段。在这个新类型上调用close时,它将在多个
Closer
s上循环,无论您有3个还是300个,然后将它们全部关闭。比如说,像这样:

type Closer interface {
    Close()
}

type multiCloser struct {
    cc []Closer
}

func (mc *multiCloser) add(c Closer) {
    mc.cc = append(mc.cc, c)
}

func (mc *multiCloser) Close() {
    for _, c := range mc.cc {
        c.Close()
    }
}
type MyStruct struct {
    // ...
    mc *multiCloser
}

func (ms *MyStruct) Close() {
    ms.mc.Close()
}

func CreateMyStructWithR1R2R3() (ms *MyStruct, err error) {
    ms = &MyStruct{mc: &multiCloser{}}
    defer func() {
        if err != nil {
            ms.Close()
        }
    }()

    if ms.r1, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r1)

    if ms.r2, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r2)

    if ms.r3, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r3)

    return ms, nil
}
这样,
CreateMyStructWithR1R2R3
的实现如下所示:

type Closer interface {
    Close()
}

type multiCloser struct {
    cc []Closer
}

func (mc *multiCloser) add(c Closer) {
    mc.cc = append(mc.cc, c)
}

func (mc *multiCloser) Close() {
    for _, c := range mc.cc {
        c.Close()
    }
}
type MyStruct struct {
    // ...
    mc *multiCloser
}

func (ms *MyStruct) Close() {
    ms.mc.Close()
}

func CreateMyStructWithR1R2R3() (ms *MyStruct, err error) {
    ms = &MyStruct{mc: &multiCloser{}}
    defer func() {
        if err != nil {
            ms.Close()
        }
    }()

    if ms.r1, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r1)

    if ms.r2, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r2)

    if ms.r3, err = OpenResource(); err != nil {
        return nil, err
    }
    ms.mc.add(ms.r3)

    return ms, nil
}

更好的是,这样您已经有了结构的
Close
函数更好的是,这样您已经有了结构的
Close
函数