我有以下基本代码:

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() { /* ... */ }

但是,发生错误时,Close()函数中的Create()调用很难看并容易出错。

我想到的另一种方法是:
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格式化程序出现时,这会使代码更长一些。

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

最佳答案

您也许可以将Close调用推送到*MyStructClose:

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
}

关于go - 具有多个步骤的结构初始化程序可能会失败,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48151651/

10-11 16:52