在Golang中使用结果模式(Result Pattern)处理错误
结果模式(Result Pattern)是一种编写能够处理不同类型结果(如成功、失败或异常)的错误容忍代码的方式。它经常被用在函数式编程语言中,但也可以使用自定义结构体、泛型和枚举在Go中实现。
结果模式可以避免使用异常进行控制流程,而是返回一个包含操作结果和任何相关信息(如消息或异常)的结构化对象。在Go中没有异常,而结果模式为开发者提供了一种友好的处理错误的方式。
通过结果模式可以轻松地检查操作的状态并相应地处理它。
目前在Go中,能够使用结果模式的方法,这要归功于函数的多返回值。Go操作通常以元组形式返回一个值和一个错误。
定义结果结构体
首先,创建一个只有两个值,Success和Failure的枚举。通过枚举有助于清晰的获得执行的结果。
type State int const ( Success State = iota Failure )
随后,创建模拟结果模式的结构体,定义三个私有属性,并且定义了获取和设置这些属性值的方法。
注意,该结构体已被定义为能处理泛型返回值和错误类型。这样做是为了使Result结构体在所有应用层和操作中都可以复用。
通过Fault泛型类型可以使用Go的error类型或者自定义错误。
对于状态,这里只获取当前值,当使用SetValue()赋值或者使用SetError()设置错误时,它就会被设定。
type Result[Output any, Fault any] struct {
state State
fault Fault
value Output
}
func (result *Result[Output, Fault]) SetValue(value Output) {
result.value = value
result.state = Success
}
func (result *Result[Output, Fault]) Value() Output {
return result.value
}
func (result *Result[Output, Fault]) SetFault(failure Fault) {
result.fault = failure
result.state = Failure
}
func (result *Result[Output, Fault]) Fault() Fault {
return result.fault
}
func (result *Result[Output, Fault]) State() State {
return result.state
}
这样也可以扩展错误处理的功能,例如,在SetError()函数中每次创建错误时都在应用日志中添加一条条目。
在Go的结果模式中使用自定义错误类型
为了展示在Go中使用自定义错误类型的结果模式的例子,创建了一个DomainError结构,它有两个属性,一个是DomainErrorType类型的,这是一个自定义枚举,另一个是使用Go生成的包含错误的error类型。
type DomainErrorType int
const (
NetworkRequest DomainErrorType = iota
JsonParsing
Storage
)
// Our custom error type
type DomainError struct {
Type DomainErrorType
Error error
}
现在来看一些使用示例,第一个是成功的操作,其他的则展示了带有自定义错误响应的失败和带有error响应的失败。
成功响应
如果一切顺利,函数会返回一个string。
func testSuccessOperation() *Result[string, DomainError] {
result := new(Result[string, DomainError])
result.SetValue("Hola a todos, esto ha ido muy bien")
return result
}
...
testOk := testSuccessOperation()
switch testOk.State() {
case Success:
fmt.Printf("👍: %s\n", testOk.Value())
case Failure:
fmt.Printf("🚨: %v\n", testOk.Fault())
}
控制台打印结果如下:
👍: Hola a todos, esto ha ido muy bien
自定义错误响应
如果在应用程序执行期间发生错误,则下面的函数会返回一个自定义的DomainError。
func testFailureOperation() *Result[string, DomainError] {
result := new(Result[string, DomainError])
myError := DomainError{
Type: Storage,
Error: errors.New("Database service unavailable"),
}
result.SetFault(myError)
return result
}
...
testFailure := testFailureOperation()
switch testFailure.State() {
case Success:
fmt.Printf("👍: %s\n", testFailure.Value())
case Failure:
fmt.Printf("🚨: %v\n", testFailure.Fault().Error)
}
程序打印信息如下:
🚨: Database service unavailable
Go error返回
当程序发生未知error时,下面的代码会返回一个error类型。
func testFailureWithErrorType() *Result[string, error] {
result := new(Result[string, error])
goError := errors.New("This is an error using the Go `error` type")
result.SetFault(goError)
return result
}
...
testGoError := testFailureWithErrorType()
switch testGoError.State() {
case Success:
fmt.Printf("👍: %s\n", testGoError.Value())
case Failure:
fmt.Printf("🚨: %v\n", testGoError.Fault())
}
程序打印信息如下:
🚨: This is an error using the Go `error` type
小节
正如前面提到的,Go使用多返回值提供了结果模式的实现,但是如果需要向错误管理添加额外功能,可以使用带有泛型的结构来实现这个模式。

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接
本文链接:https://www.choupangxia.com/2024/01/01/golang-result-pattern/