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
函数