如何避免包装函数中的动态断言?
How to avoid dynamic assertion in wrapper functions?
在下面的简化代码示例中,我们使用供应商提供的 Go 库执行了数十个不同的 API 调用。
供应商库为我们提供了API个调用体的定义:
// body of api call A
type requestBodyA struct {
aa string
bb int32
}
// body of api call B
type requestBodyB struct {
id string
}
以及为我们执行调用的函数:
// fn performs api call A
func callCreateA(body requestBodyA) {
fmt.Println("Value of body is: ", body)
// makes api call using body requestBodyA
}
// fn performs api call B
func callCreateB(body requestBodyB) {
fmt.Println("Value of body is: ", body)
// makes another api call using body requestBodyB
}
我们使用它们来创建我们自己的 CRUD 函数,我们在其中读取配置文件并执行 create/read/update/delete 调用:
func createA() {
bodyA := requestBodyA{
aa: "asdasfsd",
bb: 15,
}
// api retry loop
for i := 1; i <= 3; i++ {
// do some pre-checks (generic)
callCreateA(bodyA)
// check for errors (generic)
// check if error is recoverable (generic)
// if not return error (generic)
// if yes use the for loop and try the call again in 1,2,3,5,8,13, .. seconds (generic)
}
}
func createB() {
bodyB := requestBodyB{
id: "someUniqueId",
}
for i := 1; i <= 3; i++ {
// do some pre-checks (generic)
callCreateB(bodyB)
// check for errors (generic)
// check if error is recoverable (generic)
// if not return error (generic)
// if yes use the for loop and try the call again in 1,2,3,5,8,13, .. seconds (generic)
}
}
正如您在函数 createA
、createB
中看到的那样,我们在我们创建的每个 CRUD fn 中重复了很多通用代码(预检查、错误处理、重试循环等等)。由于我们要处理 30 多个资源并且每个资源都有自己的创建、读取、更新、销毁 API 调用函数,因此我们将相同的代码复制了 100 多次。
我想将所有通用的东西移动到一些 genericApiCaller() 并将调用主体作为一个参数和名称或指向 API 调用函数的指针作为第二个参数。
问题一:callCreateA
和callCreateB
需要不同类型的"body"。如果它们使用 interface{} 会更容易,但这些函数来自供应商 SDK,修改它们并不明智(更新原因等)。如何动态地将 genericApiCaller() 的 "body interface{}" 断言为 "body requestBodyA" 或 "body requestBodyB"?运行时断言在 Go 中是不可能的,但我没有找到任何好的解决方案。
问题 2:如何将具有不同类型参数的函数作为参数传递给 genericApiCaller()?
您可以尝试扩展供应商类型以使用类似于 CallCreate 接口的东西:
type myBodyA requestBodyA
func (b myBodyA) CallCreate() {
callCreateA((requestBodyA)(b))
}
type myBodyB requestBodyB
func (b myBodyB) CallCreate() {
callCreateB((requestBodyB)(b))
}
type CallCreate interface {
CallCreate()
}
func main() {
var ifs CallCreate
ifs = myBodyA{
aa: "aa",
bb: 15,
}
for i := 1; i <= 3; i++ {
ifs.CallCreate()
}
ifs = myBodyB{
id: "someUniqueId",
}
for i := 1; i <= 3; i++ {
ifs.CallCreate()
}
你也可以使用
func genericApiCall(api,body interface{}) {
av := reflect.ValueOf(api)
av.Call([]reflect.Value{reflect.ValueOf(body)})
}
func main() {
bodyA := requestBodyA{
aa: "asdasfsd",
bb: 15,
}
genericApiCall(callCreateA,bodyA)
bodyB := requestBodyB{
id: "someUniqueId",
}
genericApiCall(callCreateB,bodyB)
}
不过,在我看来,第二种就没那么优雅了。
在下面的简化代码示例中,我们使用供应商提供的 Go 库执行了数十个不同的 API 调用。
供应商库为我们提供了API个调用体的定义:
// body of api call A
type requestBodyA struct {
aa string
bb int32
}
// body of api call B
type requestBodyB struct {
id string
}
以及为我们执行调用的函数:
// fn performs api call A
func callCreateA(body requestBodyA) {
fmt.Println("Value of body is: ", body)
// makes api call using body requestBodyA
}
// fn performs api call B
func callCreateB(body requestBodyB) {
fmt.Println("Value of body is: ", body)
// makes another api call using body requestBodyB
}
我们使用它们来创建我们自己的 CRUD 函数,我们在其中读取配置文件并执行 create/read/update/delete 调用:
func createA() {
bodyA := requestBodyA{
aa: "asdasfsd",
bb: 15,
}
// api retry loop
for i := 1; i <= 3; i++ {
// do some pre-checks (generic)
callCreateA(bodyA)
// check for errors (generic)
// check if error is recoverable (generic)
// if not return error (generic)
// if yes use the for loop and try the call again in 1,2,3,5,8,13, .. seconds (generic)
}
}
func createB() {
bodyB := requestBodyB{
id: "someUniqueId",
}
for i := 1; i <= 3; i++ {
// do some pre-checks (generic)
callCreateB(bodyB)
// check for errors (generic)
// check if error is recoverable (generic)
// if not return error (generic)
// if yes use the for loop and try the call again in 1,2,3,5,8,13, .. seconds (generic)
}
}
正如您在函数 createA
、createB
中看到的那样,我们在我们创建的每个 CRUD fn 中重复了很多通用代码(预检查、错误处理、重试循环等等)。由于我们要处理 30 多个资源并且每个资源都有自己的创建、读取、更新、销毁 API 调用函数,因此我们将相同的代码复制了 100 多次。
我想将所有通用的东西移动到一些 genericApiCaller() 并将调用主体作为一个参数和名称或指向 API 调用函数的指针作为第二个参数。
问题一:callCreateA
和callCreateB
需要不同类型的"body"。如果它们使用 interface{} 会更容易,但这些函数来自供应商 SDK,修改它们并不明智(更新原因等)。如何动态地将 genericApiCaller() 的 "body interface{}" 断言为 "body requestBodyA" 或 "body requestBodyB"?运行时断言在 Go 中是不可能的,但我没有找到任何好的解决方案。
问题 2:如何将具有不同类型参数的函数作为参数传递给 genericApiCaller()?
您可以尝试扩展供应商类型以使用类似于 CallCreate 接口的东西:
type myBodyA requestBodyA
func (b myBodyA) CallCreate() {
callCreateA((requestBodyA)(b))
}
type myBodyB requestBodyB
func (b myBodyB) CallCreate() {
callCreateB((requestBodyB)(b))
}
type CallCreate interface {
CallCreate()
}
func main() {
var ifs CallCreate
ifs = myBodyA{
aa: "aa",
bb: 15,
}
for i := 1; i <= 3; i++ {
ifs.CallCreate()
}
ifs = myBodyB{
id: "someUniqueId",
}
for i := 1; i <= 3; i++ {
ifs.CallCreate()
}
你也可以使用
func genericApiCall(api,body interface{}) {
av := reflect.ValueOf(api)
av.Call([]reflect.Value{reflect.ValueOf(body)})
}
func main() {
bodyA := requestBodyA{
aa: "asdasfsd",
bb: 15,
}
genericApiCall(callCreateA,bodyA)
bodyB := requestBodyB{
id: "someUniqueId",
}
genericApiCall(callCreateB,bodyB)
}
不过,在我看来,第二种就没那么优雅了。