在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使用多返回值提供了结果模式的实现,但是如果需要向错误管理添加额外功能,可以使用带有泛型的结构来实现这个模式。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接
本文链接:http://choupangxia.com/2024/01/01/golang-result-pattern/